Why do everyone think SSR with Node is the only solution, when it comes to SEO with frontend frameworks?
Back in 2014 when SSR was not a thing, people were already asking “how to handle SEO with AngularJS?”.
Now, I often is see “how to handle SEO with Vue?”, and the answers are always: “use Nuxt” or “use SSR”.
Not everyone can have a Node server for their project. And there may be a lot of reasons for that: shared webhost, no root access…
But a better question is “how to handle SEO in a SPA (Single Page Application)”, and my answer is: SSR is not the only solution.
So here are 4 ways to handle SEO in 2021 with an SPA.
PS: I’ll use Vue as an example, but they all work for most frameworks.
React, Vue, Svelte… All these are frontend frameworks initially used to create SPAs, aka websites/webapps with CSR (Client Side Rendering).
What does this mean? It means the rendering is done in the browser. Therefore, the HTML sent to the browser & search engine crawlers is empty!
No HTML content = No SEO.
Usually, you have a basic index.html file with hard-coded title and meta tags.
Basic SPA entry point (index.html file)
This is how a SPA / CSR usuallly works:
SPA/CSR rendering lifecycle
In 2015 Google said they could parse JS,horray 🎉 (or not).https://developers.google.com/search/blog/2015/10/deprecating-our-ajax-crawling-scheme.
SEO with client side rendering has it’s drawbacks:
As you can see, it’s easy to setup.
You don’t need a particular server or anything, but it’s not the best way and not optimized for social media (SMO).
Especially if your content must be sharable. That’s why SSR was invented!
So SSR aka Sever Side Rendering, is a “new” concept that came with frontend frameworks. It’s based on Isomorphic programming which means the same app and code is executed on backend context and frontend context.
It was brought by Meteor JS. It’s only possible because Node uses JS.
But SSR is not so new.
It’s acatually a bad chosen name because, Server Side Rendering is what classic backend languages do since day 1.
Any backend language that renders HTML as a response to a browser request, is server side rendering.
But this “new” SSR is based on hydration, that’s what makes it special: https://ssr.vuejs.org/guide/hydration.html.
Anyway, in this method, we’ll be talking about the “new” kind of SSR that was created for frontend frameworks.
Because your app is executed on the backend, the server returns your component tree as an HTML string to the browser.
This only happens on first load. After loading, the SPA comes in, and navigation would be client-side-only like as usual.
What does this mean? Since each initial request is done by a Node server that sends HTML, this even works for social media crawlers or any other crawler.
Just like a regular monolithic app with PHP/Python/Ruby On Rails etc.
Initial browser request to a Node-based SSR server
After that, it works like a regular SPA after hydration I talked about earlier.
After the initial load, all requests go directly from the client to the API server, no need to go through the rendering server again
SSR with Vue can be done in 2 ways, DIY or with a framework on top of Vue:
You have similar stuff on React & Svelte.
You need… A Node server! Don’t worry, you only need it for the initial HTML rendering, not for your API.
Your API could be handled by any other server/backend language.
But, what if I told you that you could acheive a similar result using “regular” server side rendering (with PHP/Python/Ruby on Rails etc)?
Yes, it’s possible, let’s see how!
So, based on what we learnt in 1 & 2, we can acheive something similar with a any backend language.
What did we learn?
To solve this, we need to do 4 actions with any type of backend:
Example: let’s say I have a Vue app with 4 routes:
On my backend (Laravel in this case), I’ll have the same routes.
Backend routes mirroring
Remember, this is just an example. In real life I would have controllers of course 😅, this is just to show you the concept.
Let’s see the what the view “seoView” contains.
Backend view used as response for the initial request
That’s pretty much it for the backend, nothing more. You only need a single “view” file that takes title, meta, initialData or whatever parameters you need for SEO/SMO and that’s it.
The “window.initialData = @ json($state)” is also very important here, but not mandatory for SEO. It’s for performance/UX purposes. It’s just for you to have initial data in the frontend, to avoid an initial AJAX request to your API server.
Here’s how to use it for the /posts/:id route for example:
In a nutshell: you check if initialData exists, use it if does, or get it from the API Server if it doesn’t.
This could be done in any other lifecyle hook or method. Another way to use it could be to store the initial data in your Vuex state for example.
But all things considered, this technique works damn well and I use it in production for a very big app. Actually carrefour.fr, a Fortune 40 company in France also uses this technique with Vue JS and Sympfony (PHP).
There are some cases where, you don’t need “dynamic” rendering from the server for each request. That’s where JAMStack come’s in.
This is my the method I love the most, but it isn’t meant for all situations.
So what is JAMStack? Well it’s a fancy word for something that existed before that we called: static websites.
When the web was created, we only did pure HTML files that always returned the same content. That’s what we refer to as “static content”.
It was a bit cumbersome, that’s when they invented “dynamic websites” which is what we previously called: server side rendering.
But! Somewhere in 2008/2009, something came out and got popular in 2011/2012: Jekyll, the Static Site Generator.
Basically it was a Ruby app, that would generate static files from route definitions and data you would give to it at build-time.
That’s what we refer to as: prerendering.
It was only static files so… It was fast. Really fast.
That meant, each time you needed to update your website with fresh content, you had to prerender the website again.
No biggie. CI/CD is your friend.
JAMStack is the concept of prerendering, but automated and modernized.
In a JAMStack architecture, you would usually use a frontend framework to prerender your static files that would then turn to an SPA.
It’s mostly based on the fact that you would** rebuild pages on-the-fly anytime data changes in your APIs**, through webhooks with CI/CD.
So it’s really nice, but** not great for** websites/webapps that have daily updates with a lot of pages.
Why? Because all pages are regenerated each time.
It’s the fastest, most SEO-friendly and “cheapest” method.
You only need your API server, a static host (Netlify, Vercel, S3, Firebase Hosting, etc…), and a CI/CD system for rebuilds which you most likely already have to handle tests or deployment.
Any other SSG (static site generator) would be good but, you won’t have hydration with those not Vue-driven.
APIs: You can create your own API but, usually when you do JAMStack, it’s for content-drive websites/webapps. That’s why we often use what we call: Headless CMSs.
A headless CMS, is a CMS that can render HTTP APIs as a response.
There are many of them: Strapi, Directus (Node), WordPress (yep it can), Cockpit CMS (PHP), Contentful, Dato, Prismic (hosted)…
You can find more here: https://jamstack.org/headless-cms
There isn’t a silver bullet. It depends on your stack, budget, team, type of app and some other parameters.
In a nutshell, I would say:
That’s it. Remember: there’s never ONLY ONE WAY to do something.
Tanks for reading.
PS: If you want some time-to-time Vue/Frontend news, signup to my newsletter: https://maisonfutari.com