Optimize your SPA web app for SEO, without SSR

Alexandre Grisey
7 min readMay 15, 2024

--

SEO results before and after optmization

Or: how I reduced the loading time of my website from 600ms to less than 100ms.

I’ve recently developed a website using Vue and Firebase as main technologies. It’s called Indie Dev Tools, and it’s a directory of tools for indie developers.

I’m quite good at SEO (I even created my own Programmatic SEO technique — I’ll put a link at the end of the article), but this webapp was built differently; I wanted the SEO meta data to be loaded dynamically. Without having to use SSR (Server Side Rendering).

And that was a (big) issue.

The Root of the SEO Problem

On Indie Dev Tools, I want each tool page to be indexed by Google. Which involves to have the meta title, the description, and the other SEO elements (canonical, og, etc.) to match the tool displayed on the webpage.

So, I have to change them after the page is loaded, so I can use the product name in the title, and the product description as the page description.

The issue is, Google won’t wait for my page to be fully loaded to crawl it. For two very good reasons:

  • It probably doesn’t know when the page is fully loaded
  • Google takes the performance of the website into account to rank it; the robot probably has a timeout

The problem here is that the page was taking too long to load, and Google’s ranking robot was crawling it before the meta data had the time to change, which means Google was detecting the current page as a duplicate of my main website’s page, because of identical meta datas.

So, I could see on my Search Console that most pages were not being indexed because of duplicate canonical.

Not good.

And the loading time was not even that long! 600ms, according to PageSpeed Insights.

But still, it was too long for Google’s bots; I had to find a way to reduce it.

The Strategy I Used to Optimize my Vue & Firebase SPA Website

The first thing to do was to list what I could use to reduce that time. So I did what most developers do nowadays, when they want to do something they don’t know much about: they ask ChatGPT.

It gave me a list of recommendation, and I implemented some of them. I also implemented some of my own ideas.

I’m going to present here four of the main steps I followed to decrease the loading time, to go from 600ms to less than 100ms.

1. Clean and Reorder my Code

This one can sound quite obvious, yet it’s necessary to talk about it.

Basically, I set the SEO meta datas through a local service, that is called with the tool data as a parameter. Which works great.

But it was not optimized.

I didn’t think 600ms of loading time would be that big of a deal for Google, so I had put not that important code before the request that loads the tool data — and therefore, before the SEO elements are set.

For example, I had a line of code to scroll up to the top of the view, to make sure the user sees the beginning of the page and tool description.

But it’s totally useless, as the tool is not loaded yet. So I put this code, and all code not necessary to SEO tags setup, after the tool request is finished and the SEO service has done its work.

I could already see some improvement.

2. Reduce the Size of the Loaded JavaScript Scripts

Another suggestion I had from ChatGPT was to reduce the size of the JavaScript scripts I was importing.

The main compiled JavaScript file was 750kb, which is quite big. I opened the source file in my browser, to realize a big part of it was not minified: it was comments (licences) added by the Firebase SDK. Like, it was a lot of them.

So I searched how I could remove them, and found a way. I just added a single line in my Vite config file (yes, I’m using Vite as a web server). In the parameter of the defineConfig, I added this object:

esbuild: { legalComments: ‘none’, minifyWhitespace: true, minifyIdentifiers: true, minifySyntax: true },

The “legalComments” attribute is the most important.

Just by adding it, Vite, while building my sources, removed all the comments added by Firebase. The script file went from 750kb to 550kb(!!).

Also, to stay in the theme of loaded JavaScript, I tried to reduce as much as I could the internal services I was important in my Vue views and components.

3. Reduce and Optimize the Size of Images

This one is quite obvious, but I was (and may be you are) underestimating the importance of the size of images.

Each tool has a logo and a cover image (like, a screenshot of the landing page).

I had already limited them to respectively 1mo and 2mo.

But, especially on a mobile, these were the elements that were taking the most time to load — still according to PageSpeed Insights.

The tool was recommending to use the .webp format, and to set a proper size (I was not resizing the uploaded images).

I never used webp format, because I’ve always thought it was useless, and having a right sized .png image was good enough.

God I was wrong.

Using webp instead of png or jpeg format instantly divided the size of an image by 5, sometimes 10. I was going from more than 1mo to less than 100ko.

Also, I now resize the pictures before they’re uploaded, to match the size in which they’re displayed on the tool detail page.

For both of these implementation, I used a single npm module: compressorjs.

These few lines of code:

new Compressor(file, 
{ quality: 0.8,
mimeType: “image/webp”,
maxWidth: 256,
success(result) {
that.logo = result;
that.logoToDisplay = URL.createObjectURL(result);
},
error(err) {
console.log(err.message);
}
});

Saved like 200ms of mobile loading time.

4. Store Most Data Locally/ Fetch via a Simple GET

The last technique I came up with, I found it myself.

What is the best way to load a JSON content faster? Store it locally, or improve the request time to fetch them.

That’s what I did.

On a tool page, I was dynamically loading two JSON objects, from two different “tables” (or documents):

  • The categories, to display the one the tool belongs to, and to list them on the website’s footer
  • The tool itself

For the categories, I decided to go as simple as possible: I created a hard-coded JSON file that is stored inside of my project, like a simple constant. There is little chances that the categories change, and if they do, I’ll simply update the file!

For the tool, it was slightly more complex. It can be updated at any time by its creator, so I cannot store it inside of my source code.

The tool was stored in a Firestore database, and to retrieve it, I was doing a Firebase request (based on the id that is passed in the URL).

Instead, I decided to store the JSON as a file, in Firebase Storage. I then fetch it via a basic Axios GET request. Way faster than requesting via a database (that has to check the permission, then do the request by sorting by id and retuning the data).

Implementing these two changes reduced dramatically the loading time.

The Results of my SEO Implementation

Before the implementation of these 4 steps, 80% of my pages were not indexed by Google because of a duplicate canonical (again: the content of the page was to long to load for Google).

After the implementation, I requested Google to crawl again all these URL, and 90% of them were valid and indexed. Requesting a new crawl on the remaining ones made it 100%.

The impressions on Google went from 3 a day to 15 a day in less than a week.

And I have now almost 300 pages indexed by Google.

(impressions are very low but the website is very new).

Conclusion

SEO is a marathon, not a sprint, so it will take time to have more impressions and clicks; but I can definitely tell that this worked.

I hope this article will help you finding ways to improve your SEO, if you have a Vue and Firebase web app (although I guess it would work with other technologies!).

Like I mentioned at the beginning of the article, I created a guide about SEO Programming, that works really well if you can set your SEO meta tags statically (the content of the webpage can be loaded dynamically, but the title and description are available when the page is mounted).

Here is a link of the guide, you’ll see examples of the results on the page.

Hey! I’m Alex, a developer and solopreneur.

I create a lot of tools and products using different technologies, and write about it. Here are a few of my products:

SEO Programming: A guide to learn how to do SEO with code (and only code!).

Lite Feedback: a feedback popup to integrate on any website, with a single line of code.

And more you can find on my Linktree page !

--

--