DEV Community

Ari Koponen
Ari Koponen

Posted on • Updated on

How to Disable Server-Side Rendering (SSR) in Next.js

Learn how to disable SSR in Next.js and use it to replace Create React App or any custom tooling.


Next.js is my absolute favorite tool for developing React applications. It offers you a best-in-class developer experience. It also has a ton of useful features for being more productive and optimizing your apps in production:

  • Static & server rendering
  • TypeScript support
  • Multiple entry points
  • Bundle-splitting and tree-shaking

However, in the past, I did not want to use it for all my React apps. Next.js does not work without server-side rendering (SSR) by default. I preferred using a non-SSR solution like Create React App, when my app did not require SSR, because SSR had caused me so many unnecessary problems.

Then, one day Tanner Linsley, the author of React Query, posted on Twitter that he was using Next.js without SSR as a replacement for Create React App:

I was stoked! After a little bit of research, I was able to do this myself. 🤯

Why Disable SSR in Next.js?

SSR for React apps is necessary in a few cases:

  • The app's content needs to rank high on search results (SEO).
  • You need social-media previews (Facebook, Twitter, Slack, etc.).
  • You need the additional speed optimizations it can provide for your users.

However, using SSR has multiple trade-offs and challenges:

  • You need a complicated hosting environment. You cannot just upload your app to a server or a CDN. You need Node.js servers doing the server-side rendering. This adds complexity and also costs more.
  • You need to make sure your code works both on the browser and on the server (Node.js). This makes debugging harder and restricts you in some cases. For example, you cannot use localStorage to store authorization information, but you need to pass it in a cookie and use a cookie library that works on the server and the browser.
  • It affects your application architecture. For example, server-side rendering needs to be done in a single render, so you need to fetch all the data for the page in a single location (like getInitialProps). This requirement complicates data fetching with libraries like Redux or React Query and often leads to duplicate code.

If you don't need SSR, these trade-offs are not worth it. Basically, you should consider disabling SSR for all apps where the UI is behind a login.

Heuristic: If your app is behind a login, you likely should disable SSR.

How can you disable SSR in Next.js?

Let's go through the steps for disabling SSR for a fresh Next.js application (created with npx create-next-app).

Step 1: Rewrite All Requests to pages/index.js

Next.js supports adding redirects. Create a file named next.config.js to the root of your project. Add the following configuration there:

module.exports = {
  target: "serverless",
  async rewrites() {
    return [
      // Rewrite everything to `pages/index`
      {
        source: "/:any*",
        destination: "/",
      },
    ];
  },
};
Enter fullscreen mode Exit fullscreen mode

These redirects work only in the development environment. In production, you need to have a proxy server like NGINX or use your hosting platform's capabilities (e.g. Netlify's redirects) for doing these redirects.

Step 2: Disable SSR for Page Content

To disable SSR for page content, we need to add the following code to pages/_app.js:

import '../styles/globals.css'

function SafeHydrate({ children }) {
  return (
    <div suppressHydrationWarning>
      {typeof window === 'undefined' ? null : children}
    </div>
  )
}

function MyApp({ Component, pageProps }) {
  return <SafeHydrate><Component {...pageProps} /></SafeHydrate>
}

export default MyApp
Enter fullscreen mode Exit fullscreen mode

In the code above, we wrap our page content to a component called SafeHydrate that allows us to prevent the page content from rendering on the server. Let's go through what is happening in the code above.

With Next.js you can check if we're on the server by checking if the window object is undefined.

if(typeof window === 'undefined') {
  // This code will only execute on the server 
  // and not in the browser
}
Enter fullscreen mode Exit fullscreen mode

However, we cannot just wrap our code into this if-statement directly. If you try it, you'll notice that React will produce an annoying hydration mismatch warning in the console: Warning: Expected server HTML to contain a matching <div> in <div>. This happens, if the server HTML is different from what the browser renders.

In our case, it is safe to ignore this warning. To keep things tidy, we want to completely hide the warning from the console. This can be done by rendering a div with the prop suppressHydrationWarning. For better readability, we create a separate SafeHydrate component for this and wrap our page component into it.

Step 3: Check that Everything Works with npm run dev

Now, run npm run dev in your terminal. After the server is running at http://localhost:3000/ you should able to go to any URL (like http://localhost:3000/some/random/path) and see the content of index.js there.

Screenshot 2020-12-10 at 12.20.29

Success! 🎉

Step 4: Build production bundles with next export

We want to deploy our app as a static bundle that can be served without a Node.js server. For this, Next.js offers the command next export. It will create a static version of your app in the out directory.

To use the command, update the "build" script in your package.json like this:

"scripts": {
  ...
  "build": "next build && next export"
  ...
}
Enter fullscreen mode Exit fullscreen mode

Now, run npm run build. When you see the message Export successful, congrats! You now have a working static Next.js app in the out directory. 🎉

You can check the whole example app from this Github repository

Notes on Routing and other Advanced features

Routing

Next.js does not support dynamic routing if you don't have a server running. You need a router like react-router. The setup is the same as with other tools like Create React App.

Updating the <title> and Other <head> Tags

You don't need to add something like react-helmet for updating the head, Next.js <Head /> component will work.

Having Multiple Separate Pages

If you want, you can still use Next.js pages to have multiple different pages as separate entry points for your app. This will make your bundles per route smaller and speed up your development environment because only a part of the app will be built when you make changes.

For example, if you have a page /accounts you can create a file pages/account.js and add a corresponding rewrite:

module.exports = {
  target: "serverless",
  async rewrites() {
    return [
      // Rewrite everything under `/account/ to `pages/account`
      {
        source: "/account/:any*",
        destination: "/account",
      },
      // Rewrite everything else to `pages/index`
      {
        source: "/:any*",
        destination: "/",
      },
    ];
  },
};
Enter fullscreen mode Exit fullscreen mode

How's this different from using Next.js getStaticProps with getStaticPaths?

Using getStaticProps with getStaticPaths allows you to do Static Site Generation (SSG). This means that all the pages in your app are generated as individual .html-files when you run npm run build.

SSG is awesome, but has a single big limitation: You need to know all the paths your app has ahead of time. This is not possible with many apps that have tons of user-specific paths like /my-payments/123121521241.

With the approach described in this article, you can use a dynamic router like react-router with Next.js just like you would with Create React App or any traditional single-page app.

Additional Resources:

Top comments (18)

Collapse
 
ivan_jrmc profile image
Ivan Jeremic

If you import the component with next/dynamic you have an option which you can pass in {ssr: false }

Collapse
 
apkoponen profile image
Ari Koponen

Yes, that is useful. next/dynamic also allows you to send a smaller initial JavaScript bundle to the browser, which is great for performance!

Collapse
 
franklivania profile image
Chibuzo Franklin Odigbo

I tried using next/dynamic, and I am still getting the same errors I am getting in production. The window error.

Collapse
 
pranaypratyush profile image
Pranay Pratyush

What's the difference between this method and simply importing my main App component with next/dynamic and passing {ssr: false}?

Thread Thread
 
apkoponen profile image
Ari Koponen

Haven’t tried to do that, so not sure. Using next/dynamic might lead to one additional JS bundle.

Also, if you want multiple entrypoints (like pages/index.js and pages/account.js) you’d need to remember to do the dynamic import in each file separately.

Collapse
 
meglio profile image
Anton A.

In this scenario, what's the point in keeping using Next.js at all? No SSR, no routing, so why would one use this workaround and keep staying with Next.js instead of using something like create-react-app?

Collapse
 
apkoponen profile image
Ari Koponen • Edited

Next.js is just better than create-react-app in many small ways. It has everything that create-react-app has and more in a very developer-friendly package.

One major thing I can mention that you never need to "eject" from Next.js the way you might need to do with create-react-app. You can customize your Webpack Config and be happy (nextjs.org/docs/api-reference/next...).

Also, even if you don't need SSR now, it does not mean you won't need it in the future. Using Next.js as your tooling will allow you to use SSR/SSG for some pages and normal client-side rendered behavior SPA for others.

Collapse
 
meglio profile image
Anton A.

So, the idea is to use Next.js but not its killer features because "just in case"?

Thread Thread
 
apkoponen profile image
Ari Koponen

What do you mean by not using Next.js's "killer features"?

Collapse
 
hamzalak profile image
LAKHAL Hamza

Because you don't need SSR every time blog.usejournal.com/when-should-i-...

Collapse
 
shadowtime2000 profile image
shadowtime2000

You need a complicated hosting environment. You cannot just upload your app to a server or a CDN. You need Node.js servers doing the server-side rendering. This adds complexity and also costs more.

NextJS only does this with the next start command, it can be used with smaller serverless platforms that can reduce costs.

Collapse
 
apkoponen profile image
Ari Koponen

Correct, serverless is cheaper. Still, even a serverless platform will cost more than a CDN serving plain HTML, and even if the platform hides the complexity of doing SSR at scale, it still exists.

If you're able to use Vercel, things will work nicely out of the box. They've invested millions in having a developer-friendly platform that hides all the complexity of the underlying serverless infrastructure and tooling. 🤗

With platforms like Netlify or AWS, you'll have to do a little bit more work, but there are developer-friendly solutions like github.com/netlify/next-on-netlify and serverless.com/blog/serverless-nextjs.

However, many companies cannot use serverless solutions. I can talk from experience that running your own Node.js servers to do SSR is not very simple. Would not do it again. 😫

Collapse
 
ashutoshpw profile image
Ashutosh Kumar

Despite all the criticism to this article, I would like to thank @apkoponen for this wonderful article.

People may say that what is the use of next-js then but people coming from nuxt.js side will understand the importance of this article. Also, it helps us to get a bit better understanding of how exactly next.js works.

Thanks again @apkoponen

Collapse
 
mickmister profile image
Michael Kochell

I'm interested in trying nextjs for the DX benefits, though I've heard horror stories about SSR intricacies, and personally I don't want to learn that if I don't have to.

The redirect stuff seems like a bit of a hack. Does this make it so the user can't refresh on their current page, and they are automatically sent back to the root (or whatever you redirected to)?

Would you be able to explain how you go from a client-side-only project (like one made from CRA), and modify it to work with nextjs (with no SSR enabled on purpose)? Assume things like react-router is already set up. What are the main benefits of making the switch tool nextjs in this case?

Collapse
 
masulum profile image
Ulum

Hi @apkoponen ,

thanks for the article. it really helps us. currently we are using nextjs and we would like to run it on http server such as : nginx or apache httpd. and API also already built by other team, so we don't really need ssr in our case.

i also agree with you about nextjs in some cases better than create-react-app

Collapse
 
parkerproject profile image
Parker

Also check out this thread
stackoverflow.com/questions/531398...

Collapse
 
ajsharp profile image
Alex Sharp 🛠sharesecret.co

Worth mentioning the target option is now deprecated/removed

Collapse
 
franklivania profile image
Chibuzo Franklin Odigbo

I have been having a hosting issue because of the window error. I'd try this to see if it works. Mind that I did dynamic imports and used the undefined methods, to no awail