If you made it past the title of this blog, then it’s fair to assume you’re already au fait with Next.js. For those edge cases, Next.js is a React.js framework that gives developers the power to build fast websites. But even with such a compelling proposition, it’s unlikely your Next.js website will be fast by default, especially as your project grows. So, it’s important to understand the four different rendering techniques Next.js supports before commencing your next build.
First-things-first, when I use the term "rendering", I'm talking about the process of deriving HTML and CSS from React.js code. That is, running all the hooks and computing the HTML that makes up a page.
Likely your go-to approach for React websites, Client-Side Rendering (CSR) is exactly as it sounds, with React being rendered in the client (i.e. in the browser). Of course, it’s worth noting that CSR will always occur after the initial render of a page regardless of the initial rendering technique used. Still, in this case, we’re discussing using CSR for the initial render as well.
When a user's browser requests a page, the server will respond with an HTML document, subsequently triggering the browser to download any dependent JavaScript (JS) and CSS files. Once in the browser, React "hydrates" the page (registering the initial HTML and outputting it to the DOM), with subsequent updates made via events such as API responses and user input. Serving only static files, this approach does away with convoluted and expensive servers, giving the client complete control of how the UI is presented and managed. The downside to using CSR for your website is that JS must be enabled in the browser to see the initial state of the page. If JS is disabled, a CSR website will display as a white screen for users, which means search engine crawlers may not see your fine work either...
Before Next.js, most React websites used CSR to avoid configuring non-trivials such as Server-Side Rendering (SSR). Next.js enabled developers to write their React apps in the same way as simple CSR-only apps (e.g. using a default Create React App set-up), and the framework handled the rest. With SSR, the user's browser triggers a Node.js server to run the React code along with any hooks to generate the required HTML and initial data to fulfil the user's request. The HTML document lands in the user's browser along with any accompanying JS or CSS files required for ongoing usage of the page before CSR takes over. The benefit here is that search-engine crawlers, or users with JS disabled, will always see the initial page as intended. However, given a page might fetch all available data before sending it back to the user's browser, SSR can be slow to load, evidenced through a poor First Meaningful Paint score and frustrated users staring at blank screens for a few seconds.
A fast alternative to SSR, Static Site Generation (SSG) works by rendering the React.js page during build. That’s to say, it works by generating a set of HTML files when building and deploying a website, which can then be deployed by a hosting provider at the browser’s request. This enables the use of cheap but fast CDN caching to distribute your website, doing away with expensive servers. The downside to this approach becomes evident when updating content via a CMS. In many cases, you could only load the dynamic content of your website when the page is hydrated (rather than statically rendered), which is problematic for sites that rely on SEO. Whilst rebuilding and deploying the entire site manually (or automatically) when a change on the CMS is published is an option, this may prove too labour intensive for larger sites with frequent updates.
The release of Next.js 9.5 brought with it rendering method number four, Incremental Static Regeneration (ISR). ISR makes it possible to build SSG websites that can regenerate their pages (on a page-by-page basis) when they expire. Here’s a working example:
A developer builds and deploys a website with a configuration that defines an expiry in seconds until each page expires.
The first user to visit a page is served the initially statically rendered page.
A second user comes to the same page shortly after, and they also receive the initially statically rendered page; however, this time, it could be cached via a CDN.
Later that day, a third user visits the same page, and the time elapsed exceeds the expiry defined by the developer during build. The third user still receives the initially statically rendered page; however, this triggers a new version of the page to be generated.
A fourth user shows up a split second after user three, and because the page has not yet finished regenerating, they receive the initial statically generated page.
Seconds later, a fifth user visits the web page, at which point the page has regenerated, and any new content is visible to the user.
This process is always fast given the regeneration happens asynchronously to requests, and the CDN or server is always returning statically generated content rather than doing the work on the fly. This is all done with the known caveat that some users will receive "stale" content whilst the page is regenerating, which seems acceptable for most use cases. But this approach falls down for websites with frequent data updates such as social media feeds, where users expect their posts to be visible immediately after reloading the page. That said, this type of content is likely best fetched from the client, given SEO tends to be less critical for sites where the content happens behind a user login.
Whilst all approaches have merit, you probably don't need SSR for 99% of cases and should therefore avoid it given the headache it can induce as your website or platform scales. Instead, think of your website as a statically rendered site that can regenerate at intervals, leaning on CSR when up-to-date data is a core requirement.