Prismic Meetup

We spent 9 months refreshing our website, join us to learn from our experience.

Sign up ↗

[Decision Guide] Next.js Server-side Rendering (SSR) vs. Static Site Generation (SSG)

Written by Coner Murphy in A speed gauge for optimization Optimization on December 21, 2022
Next.js

Next.js has become a staple of a React developer’s toolkit and for many good reasons but one we’re going to focus on today is its assortment of rendering methods. In total there are four rendering methods: Client Side Rendering (CSR), Server Side Rendering (SSR), Static Site Generation (SSG), and Incremental Static Regeneration (ISR).

In this post, we’re going to be focusing on SSG and SSR because they’re the two most common and arguably most important rendering methods used in Next.js today. We’re going to compare them head-to-head to see the benefits and drawbacks of both, as well as when to use them in your applications.

A quick refresher on rendering methods in Next.js

But, before jumping into our comparison of SSG vs. SSR, it’s important we understand all of the rendering methods Next.js offers to give our comparison more context. Here is a quick overview of each one in case you’re not familiar with them.

  • CSR: In this rendering method, content is rendered in the browser on the client with JavaScript. The browser receives a bare-bones HTML document from the server and then JavaScript fills in the gaps by fetching data.
  • SSR: With SSR, all the rendering is done on the server at request-time. For every request, the server assembles the fetched data and HTML document and sends the client a complete document. The key thing to note here is the server builds and renders a new page for every request made.
  • SSG: In terms of rendering, SSG and SSR are nearly the same. The one big difference is that SSG does all of its rendering before the user requests a page. Instead, it does it at build time and fully ready files are served from a CDN. SSG builds fetched data and HTML into a page once and then reuses the output from that build for all requests to that page until the next rebuild of the site.
  • ISR: ISR addresses the one possible challenge with SSG, which is the need to rebuild your entire website to update a page’s content. With content updating frequently (e.g. on a blog), SSG might cause you to rebuild the entire site very frequently for changes that are contained in a single page. ISR allows you to rebuild only the updated pages after the site is built.
A cartoon graphics space scene showing an astronaut exploring space as rockets and planets float around.

Stay on Top of New Tools, Frameworks, and More

Research shows that we learn better by doing. Dive into a monthly tutorial with the Optimized Dev Newsletter that helps you decide which new web dev tools are worth adding to your stack.

Static Site Generation (SSG)

SSG has grown in popularity in recent years with Jamstack’s surge in popularity for many good reasons, which we’ll cover in the coming sections. But, let’s start by getting our context: what is SSG in the context of Next.js?

Well similar to the definition given earlier, it’s a way to pre-render a page's content at build time so when a user requests the page, all that happens is the CDN responds with the pre-generated HTML file.

Build time?

Build time refers to the series of steps that prepare your application code for production. For example, pre-generating pages to static HTML files.

Read more about Build time and Runtime here.

How does it work?

If you want to use SSG in your Next.js application, all you need to do is export a function called getStaticProps from the page you want to use SSG on. Then when Next.js sees this function exported it knows to generate that page using SSG when building the website using next build. You can also pass props directly to the client side page by returning a props object from the getStaticProps function.

For standard pages that have a single route, getStaticProps on its own is all you need. But, a common use case is to use SSG for content like blog posts where one page will cover multiple routes using dynamic routes in Next.js. If this is the case, you’ll also need to export a getStaticPaths function alongside getStaticProps; this allows Next.js to also pre-render all the paths specified by getStaticPaths using SSG as well. Here are the examples of these functions that Next.js provides:

 
export async function getStaticPaths() {
  return {
		// The paths to be pre-rendered at build time. E.g. /post/1, /post/2, etc
    paths: [{ params: { id: '1' } }, { params: { id: '2' } }],
  }
}

export async function getStaticProps(context) {
  return {
		// This props object will be passed to the page so they can be used on the frontend.
    props: {},
  }
}

Benefits of SSG

There are a couple of key benefits to note for why you might want to use SSG:

  • SSG pages are always online once they’re generated and distributed to a CDN. This means your backend server or data source doesn’t need to be available all the time for the page to function. Even if they go offline for a bit, your website will still be available and ready to use.
  • Pages generated with SSG are faster to use. This is because they’re already rendered on the server ahead of time so the user doesn’t need to wait for the rendering to happen on demand. Instead, they just get the completed HTML and display it.

Drawbacks of SSG

Unfortunately using SSG isn’t the solution to all problems and there are a couple of drawbacks to keep in mind.

  • On their own, SSG pages can’t change content without the site being rebuilt and redeployed entirely. So, even if you make just one small typo, the entire website needs to be regenerated and deployed to get a correction live. With a few pages, this isn’t a big issue, but as your site grows so does its time to build, which could be a problem if you need to update content often.
  • Secondly, because the pages are generated ahead of time before any requests happen, SSG pages don’t have direct access to requests like HTTP headers and query parameters at build time. If you want to access these, you have to write custom middleware in addition to SSG.

When should you use SSG?

It can be tricky to know what rendering method is best to use and for when but there are a couple of factors that help indicate that SSG is the best candidate. Let’s take a look at each of these.

If performance is a high priority and all the required data for the page is available at build time then SSG is a great choice. For example, a website using a headless CMS would be an ideal candidate for SSG. This is because all the page data is available prior to any requests being made, meaning they can be generated and cached for fast loading times.

SSG as a whole leads to very fast loading times, which while making your users happy, will also help your website’s SEO improve since search engines love fast and responsive websites that are quick for users to interact with.

However, if you have a high volume of data that updates very frequently, or if it’s critical to have the latest data available to users (like stock levels on an e-commerce site), then SSG may not be suitable because it needs a complete rebuild each time data changes. In this situation, something like ISR or SSR would be a better choice.

Server-side Rendering (SSR)

Where SSG can’t be used for some reason, people often turn to SSR because it largely solves the issues with SSG, but unfortunately, it does have some of its own drawbacks, as we’ll see in a moment. As covered earlier, SSR is similar to SSG in the sense that pages are generated on a server but the key difference is when the rendering happens. With SSR, pages are rendered at request time and for every request.

How does it work?

Setting up SSR in a Next.js application is pretty much the same as setting up SSG. All you need to do is export an async function called getServerSideProps from the page file you wish to add it to. This function is then called every time the page is requested and the output of it is the props used to generate the page.

Functionally speaking, getServerSideProps is similar to the getStaticProps function used to generate SSG pages. But, the key difference as noted above is getServerSideProps is run on every request instead of once at build time. Here’s an example from the Next.js documentation:

 
export async function getServerSideProps(context) {
  return {
		// This props object will be passed to the page so they can be used on the frontend.
    props: {},
  }
}

Benefits of SSR

While SSG is often the go-to for many people due to its own benefits, SSR poses its own unique benefits that make it a great choice as well.

  • SSR pages will always display the latest data because they are generated on the server with every request from the user. This means you can fetch data from APIs with every request to ensure you’re always getting the latest data to display.
  • If you need access to the page request for things like URL parameters or HTML headers, then SSR makes this easy since they’re all included in the request object for you to use.

Drawbacks of SSR

Unfortunately, as is the case with many things, SSR isn’t all sunshine and rainbows, and it does have a few drawbacks that need to be taken into account.

  • SSR is notably slower at displaying a page than SSG is. Vercel even documents this in the Next.js documentation. This is all because SSR generates each page per request where SSG can move that entire process to the site build, before user requests are made. During the generation process, there are also external factors that could influence your page loading speed like fetching data from external APIs. If the APIs are slow to respond, your page generation and in turn loading times will suffer.
  • Similar to the last point, using SSR will lead to higher TTFB (Time to First Byte) metrics. This is because this metric and some other Core Web Vitals metrics are measured on the initial page rendering and loading times.
  • Finally, if you want to cache your SSR pages to improve their loading times, you’ll need to implement this logic and functionality yourself. This wouldn’t be too bad until you realize that SSG pages are cached automatically in comparison.

When should you use SSR?

Even with the drawbacks, there are times when using SSR is the best choice. Let’s take a look at two of them.

Access Page Requests

If you need to access data from a page request or perform extra logic off the back of the request, like hitting an API based on a query parameter, then SSR is the clear choice since you can easily implement this functionality without needing to write custom middleware.

Request Time Data Fetching

If you need your page is based on data that can only be obtained at request time then SSR makes sense. For example, a lot of applications built using Next.js will use SSR for pages behind authentication because they can’t be pre-rendered using SSG, and they need to perform additional logic based on the requests, like checking that cookies are valid from the request headers or needing to validate data provided against a database.

SEO considerations

Although they both have their own benefits and drawbacks, for the most part, SSG and SSR are both great for SEO. This is because both of them generate their HTML outputs on the server at either build or request time. This means both can be crawled and indexed by search engine bots.

Both rendering methods also render pages quickly, which will help SEO rankings. However, out of the two, SSG does render faster so if you have particularly large pages and can use it, SSG will help more.

My rule of thumb

In closing, both SSR and SSG are great rendering methods and have their own pros and cons that make them suitable for different scenarios in your Next.js application. Overall one isn’t better than the other. It’s almost like comparing apples and pears; they have similarities but also have different purposes, so it’s important to know how and when to use both methods in your applications.

But, to help you decide on which rendering method to use and when, I generally follow this: Where possible look to use SSG, but if that’s not possible for some of the reasons above, then use SSR for the page or a combination of CSR and SSG depending on if SEO is an important consideration or not.

A portrait photo of Coner Murphy in a plaid shirt.

Coner Murphy

Web Developer, technical writer, and tech entrepreneur sharing my journey to financial freedom. Building PhyType and SaaS products in public.

More posts