NextJS SSR: Use Cases, How-To & Tricks (2023)

When React 18 announced React Server Components in March 2022, SSR became a hot topic again: client-side rendering was a major development in the web industry in the past decade, but React now wants us to go back to the server-side? What’s the point? In this article, we’ll explain what SSR is, why it’s important, and when to use it in NextJS.

At JAMstack Consulting, we know how confusing rendering strategies can be. There are new frontend frameworks every year with new rendering methods, and it’s hard to keep up with the latest trends. NextJS has evolved a lot over the years as well, so it doesn't make things easier. Hopefully, this updated article will help you understand the different rendering methods and make more informed decisions.

What’s SSR and Why It’s Important

SSR stands for "Server-Side Rendering", meaning rendering web pages on the server before sending them to the client's browser. This is in contrast to client-side rendering, where the browser downloads the necessary JavaScript and renders the web page on the client-side.

There are three main benefits to SSR:

  1. Better performance - SSR can improve the initial load time of a web page, as the server sends back a fully-rendered HTML page instead of a blank page that requires additional client-side processing.
  2. Search engine optimization (SEO) - Search engines can crawl and index server-rendered pages more easily than client-rendered pages, which improves a website's visibility and ranking in search engine results pages.
  3. Security - SSR can improve website security by reducing the risk of cross-site scripting (XSS) and CSRF attacks.

It’s often difficult to understand the difference between SSR, SSG, CSR, and incremental static regeneration, and when to use each. Let’s break it down.

When To Use SSR

NextJS proposes different rendering methods: SSR, SSG (static site generation), CSR, and Incremental Static Regeneration (ISR).

Here is a good rule of thumb to pick the right one for your use case:

  • Use SSR if a public-facing webpage requires dynamic data that is frequently updated. SSR provides a better user experience by showing content that is always up-to-date while helping with SEO. Example: a forum, a community board, a login page using CSRF tokens.
  • Use SSG if your webpage does not require frequent updates and can be pre-built. SSG provides faster load times as webpages are served as static files. Example: a blog post, a landing page.
  • Use SPA if your webpage displays frequent updates and real-time data. SPA provides a smoother user experience as the web application does not need to reload on each page view, but they can be more difficult for search engines to crawl and result in slower initial load times. Example: a private dashboard, a user account, any private webpage.
  • Use ISR if your website or web application requires frequent updates but can be pre-built. ISR serves webpages as static files that are updated incrementally as needed, like a hybrid between SSR and SSG. Example: a forum page with infrequent content changes (once an hour).

5 NextJS Features To Make The Best Of SSR

1. The getServerSideProps Method

Before NextJS 13, your only option is to use the getServerSideProps function to enable SSR in your page:

function Page({ data }) {
  return (
    ...
  )
}

export async function getServerSideProps() {
  const res = await fetch(`https://example.com/api/data`)
  const data = await res.json()

  return { props: { data } }
}

export default Page

getServerSideProps runs on the server-side whenever a user requests the page, never on the client's browser. The action pre-renders the page with the returned props.

Whenever a user requests a page that uses getServerSideProps through client-side page navigation with next/link or next/router, Next.js sends an API request to the server. This request prompts the server to run getServerSideProps, fetch any necessary data, and then pre-render the page with the fetched props.

Even though the content is server-side rendered, React components will still be hydrated at runtime so you can mix client-side and server-side rendering.

2. Caching Strategies

Server-side caching is important to improve website performance. It involves temporarily storing the rendered content on the server, so that it can be quickly retrieved and served to subsequent requests without having to re-render the content each time. Combined with a CDN, it makes distributing web content much faster!

In NextJS, you can define caching headers (Cache-Control) inside getServerSideProps to cache dynamic responses. For example, using the stale-while-revalidate caching policy:

export async function getServerSideProps({ req, res }) {
  res.setHeader(
    'Cache-Control',
    'public, s-maxage=10, stale-while-revalidate=59'
  )

  return {
    props: {},
  }
}

The most commonly used cache policies include directives like "max-age", "no-cache", "no-store", "public", and "private", but the actual values you use depend on the nature of your data. The longer the cache, the more performance. But you don’t want stale data to keep your user from accessing up-to-date information.

3. React Server Components

From Nextjs 13, you can use React Server Components in the app directory. Server components simplify using both server-side rendering and client-side rendering in your applications.

Unlike before, all components in the app directory become Server Components by default, and you need to differentiate Client components in the code with 'use client' declaration:

'use client';

import { useState } from 'react';

export default function ComponentA() {
    ...
}

The main appeal of React Server Components is to increase page speed: large dependencies that would previously impact the JavaScript bundle size on the client remain entirely on the server. You just ship the bare minimum Javascript needed to keep things interactive on the client side, asynchronously and on an as-needed basis.

4. Streaming SSR

Streaming allows rendering parts of the UI incrementally to the client. It’s the equivalent of lazy loading for components.

Server-side rendering (SSR) has sequential and blocking steps, which means the server can render HTML for a page only after fetching all the data. React then re-hydrates the UI, making it interactive. While SSR helps to show a non-interactive page quickly, it can still be slow as all data fetching must complete before displaying the page.

Streaming in React and Next.js breaks down the page's HTML into smaller chunks and progressively sends those chunks from the server to the client. Higher-priority components can be sent first, allowing React to start hydration earlier, while lower-priority components can be sent later.

Streaming helps prevent long data requests from blocking page rendering, especially on slower devices.

There are two ways to implement Streaming SSR in NextJS: with the loading method, or with the Suspense component.

The loading.js file displays a loading component while the content of a route in the same folder loads:

export default function Loading() {
  return <p>Loading...</p>
}

At the component level, you can React Suspense. Just wrap the SSR component and it’ll display a loading UI when its state changes.

import { Suspense } from "react";
import { PostFeed, Weather } from "./Components";

export default function Posts() {
  return (
    <section>
      <Suspense fallback={<p>Loading feed...</p>}>
        <PostFeed />
      </Suspense>

      <Suspense fallback={<p>Loading weather...</p>}>
        <Weather />
      </Suspense>
    </section>
  );
}

5. Edge and Node.js Runtimes

Next.js has two server runtimes with different features available to render parts of your application: Node.js and Edge.

Each runtime has its own set of APIs. By default, the app directory uses Node.js, but you can choose different runtimes for each route in your app using the runtime variable:

export const runtime = 'experimental-edge'; // or 'nodejs' by default

The Node.js runtime gives you access to all the Node.js APIs and npm packages you might need, at the expense of a slower startup time.

The Edge Runtime is a subset of Node.js, so it’s best if you need lower latency for smaller, simpler functions that do not exceed 4 MB.

Consult With Us

Hope it helped you understand the five key features to enable SSR in your NextJS app! NextJS is getting better and better at blurring the line between SSG and SSR, and we're excited to see what's coming next. Don't hesitate to get in touch with us if you need help to choose a or implement rendering methods―it's our job!