Using SWR React Hooks With Next.js’ Incremental Static Regeneration (ISR)

Quick summary ↬ When paired with ISR and Next.js’ API routes, SWR can be used to create a responsive user experience. In this article, Sam Poder explains what SWR is, where to use it (and where not), and how to build a website by using Incremental Static Regeneration.

If you’ve ever used Incremental Static Regeneration (ISR) with Next.js, you may have found yourself sending stale data to the client. This occurs when you are revalidating the page on the server. For some websites this works, but for others (such as Hack Club’s Scrapbook, a site built by @lachlanjc that I help maintain), the user expects the data to be kept up to date.

The first solution that comes to mind may be to simply server side render the pages, ensuring that the client is always sent the most up to date data. However, fetching large chunks of data before rendering can slow down the initial page load. The solution used in Scrapbook was to use the SWR library of React hooks to update the cached page from the server with client side data fetching. This approach ensures that users still have a good experience, that the site is fast and that the data is kept up to date.

Meet SWR

SWR is a React Hooks library built by Vercel, the name comes from the term stale-while-revalidate. As the name suggests, your client will be served stale/old data whilst the most up to date data is being fetched (revalidating) through SWR on the client side. SWR does not just revalidate the data once, however, you can configure SWR to revalidate the data on an interval, when the tab regains focus, when a client reconnects to the Internet or programmatically.

When paired with ISR and Next.js’ API routes, SWR can be used to create a responsive user experience. The client is first served the cached statically generated page (generated with getStaticProps()), in the background the server also begins the process of revalidating that page (read more here). This process feels fast for the client and they can now see the set of data, however it may be a touch out of date. Once the page is loaded, a fetch request is made to an Next.js API route of your’s which returns the same data as what was generated with getStaticProps(). When this request is completed (assuming it was successful), SWR will update the page with this new data.

Let’s now look back at Scrapbook and how this helped solve the problem of having stale data on the page. The obvious thing is that now, the client gets an updated version. The more interesting thing, however, is the impact on the speed of our side. When we measure speed through Lighthouse, we get a speed index of 1.5 seconds for the ISR + SWR variant of the site and 5.8 seconds for the Server Side Rendering variant (plus a warning regarding initial server response time). That’s a pretty stark contrast between the two (and it was noticeable when loading the pages up as well). But there is also a tradeoff, on the Server Side Rendered page the user didn’t have the layout of the site change after a couple of seconds with new data coming in. Whilst I believe Scrapbook handles this update well, it’s an important consideration when designing your user’s experience.

More after jump! Continue reading below ↓
Smashing Email Newsletter with useful tips on front-end, design & UX. Subscribe and get “Smart Interface Design Checklists” — a free PDF deck with 150+ questions to ask yourself when designing and building almost anything.

Feature Panel

Where To Use SWR (And Where Not To)

SWR can be put in place in a variety of places, here are a couple of site categories where SWR would make a great fit:

One thing to note, is that these can all be applied with or without ISR.

There are of course some places where you won’t want to use SWR or to use SWR without ISR. SWR isn’t much use if your data isn’t changing or changes very rarely and instead can clog up your network requests and use up mobile user’s data. SWR can work with pages requiring authentication, however you will want to use Server Side Rendering in these cases and not Incremental Static Regeneration.

Using SWR With Next.js And Incremental Static Regeneration

Now we’ve explored the theory of this strategy, let’s explore how we put it into practise. For this we’re going to build a website that shows how many taxis are available in Singapore (where I live!) using this API provided by the government.

Project Structure

Our project will work by having three files:

Our helpers file will export a function (getTaxiData) that will fetch the data from the external API, and then return it in an appropriate format for our use. Our API file will import that function and will set it’s default export to a handler function that will call the getTaxiData function and then return it, this will mean sending a GET request to /api will return our data.

We’ll need this ability for SWR to do client-side data fetching. Lastly, in our frontend file we’ll import getTaxiData and use it in getStaticProps, it’s data will be passed to the default export function of our frontend file which will render our React page. We do this all to prevent code duplication and ensure consistency in our data. What a mouthful, let’s get started on the programming now.

The Helpers File

We’ll begin by creating the getTaxiData function in lib/helpers.js:

export async function getTaxiData(){ let data = await fetch("").then(r => r.json()) return {taxis:[0].taxi_count, updatedAt:[0].timestamp}

The API File

We’ll then build the handler function in api/index.js as well as importing the getTaxiData function:

import { getTaxiData } from '../../lib/helpers'
export default async function handler(req, res){ res.status(200).json(await getTaxiData())

There isn’t anything here unique to SWR or ISR, besides the aforementioned project structure. That stuff starts now in index.js!

The Front-End File

The first thing we want to do is create our getStaticProps function! This function will import our getTaxiData function, use it and then return the data with some additional configuration.

export async function getStaticProps(){ const { getTaxiData } = require("../lib/helpers") return { props: (await getTaxiData()), revalidate: 1 }

I’d like to focus on the revalidate key in our returned object. This key practically enables Incremental Static Regeneration. It tells your host that every one second regenerating the static page is an available option, that option is then triggered in the background when a client visits your page. You can read more about Incremental Static Regeneration (ISR) here.

It’s now time to use SWR! Let’s import it first:

import useSWR from 'swr'

We’re going to be using SWR in our React-rendering function, so let’s create that function:

export default function App(props){

We’re receiving the props from getStaticProps. Now we’re ready to set up SWR:

const fetcher = (...args) => fetch(...args).then(res => res.json())
const { data } = useSWR("/api", fetcher, {initialData: props, refreshInterval: 30000})

Let’s break this down. Firstly, we define the fetcher. This is required by SWR as an argument so that it knows how to fetch your data given that different frameworks etc. can have different set ups. In this case, I’m using the function provided on the SWR docs page. Then we call the useSWR hook, with three arguments: the path to fetch data from, the fetcher function and then an options object.

In that options object, we’ve specified two things:

The initial data option is where we provide the data fetched from getStaticProps which ensures that data is visible from the start. Lastly, we use object destructuring to extract the data from the hook.

To finish up, we’ll render that data with some very basic JSX:

return <div>As of {data.updatedAt}, there are {data.taxis} taxis available in Singapore!</div>

And, we’ve done it! There we have a very barebones example of using SWR with Incremental Static Regeneration. (The source of our example is available here.)

If you ever run into stale data with ISR, you know who to call: SWR.

Smashing Editorial (vf, yk, il)