All articles

Selectively enable SSR or SPA mode in a Nuxt.js 2 app

Updated at April 18, 2020

— 3 min read

Attention! This post hasn't been updated for more than a year and could be outdated!

This article is written for Nuxt 2.

If you are looking for the Nuxt 3 version of this article, please follow this link to the updated Nuxt 3 version.

Hey Nuxt friends! As you might already have experienced, SSR comes with certain caveats, including no access to browser-specific APIs, like the local storage, on the server-side. But what if you could enable SSR only for pages where SEO is needed and use the “traditional” SPA mode elsewhere, say in a “member area” in your Nuxt 2 application.

Guess what? You can do that! In the following post I’ll show you how to selectively disable SSR or SPA mode based on the page url.

The Nuxt Source Code

Before going into detail on how to enable SSR selectively we should look into the Nuxt.js source code to see how Nuxt makes such a distinction possible. The responsible snippet can be found in the vue-renderer package. Let’s dissect it one line after each other!

  1. Extract req and res (the request and response object from the request to the server) from the context object.
const { req, res } = context
  1. If context.spa (might have been set through other internals before rendering pages) or res.spa (can be modified otherwise!) is truthy, treat the page that’ll be rendered as SPA.
const spa = context.spa || (res && res.spa)
  1. In case SSR is disabled or the page should be treated as SPA, only load metadata and render the page as SPA (with JavaScript files but no HTML included) through an early return.
if (!this.SSR || spa) {
  const {/* ... */} = await this.renderer.spa.render(context)
  const html = this.renderTemplate(/* ... */)
  return { html, getPreloadFiles: this.getPreloadFiles.bind(this, { getPreloadFiles }) }
}

And that’s all the magic ☺️

Implementation

After looking into the source code we found out that all we have to do is to set res.spa for the pages where we want to disable server side rendering. This only has to happen on the server-side because a Nuxt app will behave like a traditional SPA on client-side anyway. If we think about server-side only manipulation, the first thing that should come into our minds should be serverMiddleware (see also: Sending Emails Through Nuxt.js via serverMiddleware). Using them comes with two major benefits:

  1. serverMiddleware are a concept that is directly provided through the framework (no 3rd party libs needed)
  2. We can freely customize our selection logic.

A minimalist implementation would look like this:

export default function(req, res, next) {
  const paths = ['/', '/a']

  if (paths.includes(req.originalUrl)) {
    // Will trigger the "traditional SPA mode"
    res.spa = true
  }
  // Don't forget to call next in all cases!
  // Otherwise, your app will be stuck forever :|
  next()
}

To see a working example directly, you can check out the CodeSandbox. Be aware that it runs in dev mode so the difference between SPA and SSR isn’t that huge but still distinguishable via process.server.

Wrapping up

You might ask yourself: Do I need to selectively switch between the modes? The answer is most of the time: No. But there might be some situation where you want to avoid wrapping a lot of page contents in <ClientOnly> tags (especially for dashboards or member areas) that are part of your SEO-heavy app. It might be a niche but it has it’s use cases.

What did you use the selective SPA/SSR for? Please tweet me at @TheAlexLichter, reach out on the Vue/Nuxt Discord or write me a mail (blog at lichter dot io).

I hope you enjoyed the article and the small Nuxt source code dive. If this is the case, I’d gladly ask you to spread the word!

Tweet this article

Originally published at December 29, 2018

Photo of Alexander Lichter

Written by Alexander Lichter

I'm Alex, a German web engineering consultant and content creator. Helping companies with my experience in TypeScript, Vue.js, and Nuxt.js is my daily business.

More about me