SEO in Times of Headless CMSs and SPAs

Contents

Oh dear, these are a lot of abbreviations for a single heading. To make it more clear, this article is about search engine optimization (SEO) in the age of headless content management systems (CMSs) and single page applications (SPAs).

First of all let’s take a look at what’s so special about headless CMS powered SPAs in regards to SEO.

Headless CMSs and SEO

The only big difference between a traditional CMS and a headless CMS like Storyblok, when it comes to SEO, is that most traditional CMSs allow you to edit site metadata such as titles and descriptions out of the box. Since a headless CMS has no control over how the content is rendered, we have to add this functionality ourselves. In this article we’ll take a look at what we can do to make Storyblok at least as or maybe even more SEO friendly than WordPress or Drupal.

SPAs and SEO

When it comes to the SPA architecture usually we mean client side rendered JavaScript applications. Considering that Google is capable executing JavaScript when crawling websites since 2014, at first glance, there seems to be nothing special about single page applications in terms of SEO. In this article we’ll take a look at some of the pitfalls you can run into when building JavaScript applications that must be SEO friendly and how to avoid them.

SEO enhancements in Storyblok

Usually all what a CMS can provide in terms of SEO is a good foundation which means being able to specify the page title and meta description. Additionally you also want to specify alt text for your images, not only because of SEO but even more importantly to make your site more accessible.

Metadata plugin

Let’s start with the most essential things first. In order to make it possible to specify metadata like a page title and a meta description, we can use the meta-fields plugin. In order to enable the meta-fields plugin, you have to add a new field to the schema of the component of your page type.

Adding a new metadata field.

After adding the new metadata field to the schema of your content type, your content editors can use it to add title and description texts.

Adding metadata.

<!doctype html>
<html class="no-js" lang="">
<head>
  <meta charset="utf-8">
  <title>{{ metadata.title }}</title>
  <meta name="description" content="{{ metadata.description }}">
  <!-- ... -->
</head>
<!-- ... -->

Above you can see a generic example how to use data from the metadata field in your HTML.

Alt texts for images

Additionally we also want to specify alt texts for all of our images. In order to make this possible, we create a new image component.

Image component with alt text field.

As you can see above, I choose paragraph_image as the technical name for the component. This is because I use the Story, Chapter and Paragraph naming scheme you can read about in my article about how to structure content in Storyblok.

{
  "image": {
    "alt": "A beautiful sunset.",
    "src": "//a.storyblok.com/f/123/123/sunset.jpg"
  }
}

This is the data you’ll get back from the Storyblok API. Next you can see a generic code snippet of how to use it in your app.

<img alt="{{ image.alt }}" src="{{ image.src }}">

Server side rendering

Although nowadays Google is able to crawl client side rendered applications, there are still a few reasons why server side rendering (SSR) might be preferable.

  1. Most other search engines still don’t crawl client side rendered content.
  2. The Google Bot might run on an old version of Chrome which is not supported by your app.
  3. Many SEO experts remain skeptical wether JavaScript rendering will be penalized or not.

Let’s face it, Google dominates the search market. But even if 95% of search engine traffic comes through Google, are you really willing to give up the remaining 5%?

The second point on the list is an even more important one. Server side rendering is much more resilient than client side rendering. While HTML is extremely fail-safe and most browsers (and crawlers) can even interpret extremely faulty HTML code to some extent, a single error in your JavaScript code is all it takes to make it impossible for the Google Bot to crawl the page. If you don’t want to add the Google Bot to the list of browsers you have to actively support, there is no way around SSR.

It’s hard to tell if Google makes any difference between traditional websites and websites built using a SPA architecture. So the third point is kinda vague. But in the same way as you don’t ask any questions and follow the man from the bomb squad as he runs past you, you better use SSR when client side rendering makes the SEO person nervous.

What you can do to learn more about how Google sees your site though, is to use the URL Inspection Tool in the new Google Search Console.

Google URL Inspection Tool site preview.

How to implement SSR

Going into all the details of SSR would go beyond the scope of this article. There are tools like the webpack Prerender SPA Plugin which you can use to upgrade your existing SPA to be more SEO friendly. If you happen do be a Vue.js user you can read my article about how to use the Prerender SPA Plugin in combination with Vue.js.

Furthermore, here is a list of some of the most popular tools with SSR already baked in.

React

Vue.js

Performance

Nowadays (loading) performance is one of the most important ranking factors. Google is pushing very hard to make the web faster. They even provide a very useful tool for checking the loading performance of your website: Lighthouse. I highly recommend you to run a Lighthouse audit and to follow the instructions it gives you. Let’s take a look at some of the most important things you can do to improve the performance of your SPA.

Code splitting

In order to make your app load fast, one of the most straightforward solutions is to only serve what absolutely is needed to render the current page. You can do this by applying code splitting techniques to your application. In the following example code snippet you can see how you can lazy load certain routes in a Vue.js application.

// See: https://router.vuejs.org/guide/advanced/lazy-loading.html
const Foo = () => import('./Foo.vue')

const router = new VueRouter({
  routes: [
    { path: '/foo', component: Foo }
  ]
})

If you’re on team React, you can read more about how to use code splitting in React apps in the official documentation.

Image Service

Another tool which is provided to us by Storyblok, is the powerful Image Service. We can use this to automatically generate highly optimized images in the exact sizes we need them. This helps us to deal with one of the most important on-site SEO optimizations: making our sites as fast as possible.

<!-- high resolution / retina images -->
<img
  src="https://img2.storyblok.com/370x370/f/46288/76976577fa/b_149.jpg"
  srcset="https://img2.storyblok.com/740x740/filters:quality(40)/f/46288/76976577fa/b_149.jpg 2x"
  alt="A demo image."
>

<!-- srcset with sizes -->
<img
  src="https://img2.storyblok.com/370x370/f/46288/76976577fa/b_149.jpg"
  srcset="
    https://img2.storyblok.com/200x200/f/46288/76976577fa/b_149.jpg 200w,
    https://img2.storyblok.com/370x370/f/46288/76976577fa/b_149.jpg 370w,
    https://img2.storyblok.com/600x600/f/46288/76976577fa/b_149.jpg 600w
  "
  sizes="50vw"
  alt="A demo image."
>

As you can see in the examples above, we can use the Storyblok Image Service to automatically generate images in a variety of sizes, so that regardless of the screen size of the user’s device, an image with the ideal dimensions is loaded.

Lazy loading images

Even better than loading the correct sizes of your images is not loading images at all if they’re not needed (because they are not above the fold for example). We can use lazy loading to prevent images from loading immediately and to automatically load them as soon as they become visible instead. One pretty powerful and lightweight solution for this is Lozad.js. You can read more about how to use Lozad.js to lazy load images in a Vue.js application in my article about this topic.

Wrapping it up

Many CMSs, and even more plugins for those CMSs, claim to make your website SEO friendly out of the box. Just install this or that plugin and your site will rank number one on Google the next day. Of course no CMS and no plugin can do that for you. SEO is hard work and this article only scratches the surface. There is nothing special about headless CMSs which makes them inherently bad for SEO. The same is true for SPAs.

There are some things to keep in mind that are the foundation for building websites that are SEO friendly. Everything else is a lot of hard work and sometimes even just trial and error. But if you make sure the Google Bot is able to crawl your content (SSR), you add some basic metadata and you also make your site as fast as possible, the chances are good that before long your content will be ranked well.

To sum things up: traditional CMSs provide a bare minimum of SEO friendliness out of the box. With headless CMSs we have to do some of the leg work ourselves. But because we have full control over the final markup there is also a huge potential to build very performant websites which are perfectly optimized for being crawled by the Google Bot and other search engine crawlers.


More to read...

About the author

Markus Oberlehner

Markus Oberlehner

Markus Oberlehner is a Open Source Contributor and Blogger living in Austria and working at karriere.at. He is the creator of avalanche and node-sass-magic-importer.