The web development community is rapidly adopting Single Page Applications (SPAs) for various web projects. React, for example, has seen a 1633% increase in projects using SPAs from 2018 to 2021 (according to Builtwith).

Because SPAs are fundamentally built in JavaScript, they often create problems for search engines trying to crawl the website. In addition, they can be extremely resource-intensive on browsers, resulting in a slow experience and negatively impacting conversions.

Fortunately, there have been improvements made in frameworks for speed, and Google has improved its Web Rendering Service over time. However, there are still actions you can take to make further improvements.

With Google making changes to what it suggests for JavaScript websites, it would be good to revisit how your Single Page Application reconciles SEO by delivering its content to Google.

But what are the best ways to serve content to SPAs to both search engines and users?

Server-side rendering

Server-side rendering is the process of rendering the full HTML + CSS of your SPA before sending it to a client. This is usually without the accompanying JavaScript making it interactive, as sending that JavaScript would create hydration. Compared to dynamic rendering and hydration, all content is rendered through the server before being sent to search engines and users.

This removes all the complexities of dynamic rendering, showing search engines exactly what you would show to users instead of serving two separate versions that can have different representations.

In addition, it can return and improve Core Web Vital performance for your SPA compared to the other options, as users’ devices wouldn’t need to process any JavaScript to render content.

Server-side rendering, however, requires a server to process all rendering requests — both from search engines and users — which can be extremely resource-intensive depending on how the SPA has been built, potentially being a highly costly approach.

Dynamic rendering

Dynamic rendering is the process of serving a pre-rendered/server-side rendered version of a website to bots and the client-side rendered content to users. Google has recently come out to say this is a workaround and not a long-term solution, recommending other solutions.

Realistically, moving away from dynamic rendering can be extremely hard, as the other alternatives can be extremely costly or are long-term projects. Dynamic rendering does not penalize your website unless you serve completely different content to Google and users. However, Google has stated that “it creates additional complexities and resource requirements”.

Dynamic rendering, when done right, allows you full control of what to present to search engines. This can be especially helpful for removing unnecessary user functionality elements to search engines, allowing for a clearer view of content.

Dynamic rendering also does nothing to help speed performance for users, as the content must rely on the user’s device to render, which generally takes much longer than the other solutions outlined.

This results in lower Core Web Vitals scoring, where the Largest Contentful Paint (LCP) is usually the biggest offender and scores websites into the “Poor” category. This puts it at a disadvantage in Google’s rankings if other competitors perform better in Core Web Vitals.

Optimization efforts could be undertaken, to help improve load time and LCP, but this would make more sense to be actioned in line with scaling out a different rendering option.

Static rendering

Static rendering is the process of producing individual HTML files for each separate URL. However, this happens only once at build time, rather than server-side rendering, when the user or search engine requests the file. This is commonly found on JavaScript frameworks like Gatsby, Next.JS, and Astro.

These files can then be stored on your CDN (Content Delivery Network) or general cloud infrastructure, reducing costs compared to server-side rendering.

In addition, as the HTML files are stored on a CDN, these pages are loaded much quicker than they would when using server-side rendering while still hydrating them, resulting in much higher CWV scoring. They are also interactive without requiring much JavaScript to be executed or hydrated.

As the HTML files can be stored on an edge server, this would not incur costs like server-rendering would.

Static rendering, however, would only be viable for pages with ‘static’ content (e.g. privacy policy, ToS) and not something much more dynamic and frequently changing (e.g. ‘Today’s hot news’ on news websites).

With all the above solutions, hydration is required to make any user-specific elements interactive.

Hydration

Hydration is the process of checking the DOM (Document Object Model) nodes on a webpage that require interactivity and attaching the necessary handlers to make them interactive. All JavaScript hydration requires some server-side rendering that allows for all desired content to be delivered to the user.

The DOM nodes that would require hydration would be elements like search functionality, facet filters, navigation carousels, etc., allowing them to interact with the user. Although these would not specifically impact rankings for Google, the search engine still wants to see exactly what the user would see where possible.

Two approaches to hydration are often recommended: Full Page Hydration and Partial Hydration.

Full Page Hydration is where the framework process the full DOM to check to add interactivity where necessary, reconciling the rendered state with the application state.

Partial hydration is a more flexible option. Only certain parts of the DOM — dynamic sections called ‘islands’ — are processed by the framework to add interactivity. This option requires much less processing as only certain specified DOM nodes are checked rather than all DOM nodes.

Partial and Full Hydration
Difference between full hydration and partial hydration

Hydration is not always an independent rendering solution. It is a process that can take place with SSR or static rendering to add user interactivity.

Depending on your website setup, hydration can be required to render the page’s core content. For example, the shell of your website (e.g. navigation bar) can be server-side rendered, and user interactivity is required for the core content of the page — take those online newspaper simulator websites, for example. In this case, hydration is required to render the core content – which would be relying on Google to render it.

Google proclaims that it can render and “execute JavaScript”. As we know, Google can execute JavaScript, but not very well. This brings the risk of Google potentially not rendering the content on your pages, which can severely disrupt rankings if Google decides to index the non-rendered version of your pages.

This is not the same for other search engines either. Bing also has limited JavaScript rendering resources — using Chromium-based Edge to render JS – and international search engines would have even fewer JavaScript rendering capabilities.

Other hydration approaches, like progressive hydration, can also be applied, which can help with speed metrics (e.g. Time To Interactive).

Which one to use?

Which rendering solution to use would be entirely dependent on your situation.

For large-scale websites that have frequently updated on-page content (e.g. news websites), server-side rendering is the way to go for your pages.

Alternatively, if you use a framework like Gatsby or Astro and don’t frequently update the content on your website (excluding sections like a blog where posts can be added with a new build from the headless CMS), static rendering will be the way to go.

Dynamic rendering, however, should only be seen as a short-term solution, as Google ultimately wants to see exactly what the user sees.

Hydration will happen naturally for both rendering solutions if your website has any user interactivity features, though different approaches can be taken to help increase speed for users.