January 20, 2021

Updating Static Next.js Pages Instantly

Lightning-fast pages, instantly updated

For the last few months, I've been working full-time on Give&Bake; allowing users to share their favourite recipes with their favourite people. Recipes that need to be fast, secure and always online. With static generation built-in, Next.js was the tool of choice.

Incremental Static Generation

Incremental Static Generation (ISR) is one of my favourite features of Next.js; providing all the benefits of static pages, with the ability to update pages in the background as traffic comes in.

On paper it sounds like ISR would work perfectly for Give&Bake's use-case, but there is an important caveat to consider. When a user visits a page with ISR enabled, an update will be triggered in the background for the next (it's in the name) user, not the current user.

If a user edited their recipe on Give&Bake, they wouldn't see their changes on the static recipe page until they hit refresh; a far from ideal user experience.

A Sprinkle of SWR

SWR ("stale-while-revalidate") is another great little library from the folks at Vercel, commonly used to fetch and revalidate data on the client-side.

With the initialData option we can skip the fetching altogether, and pre-fetch via ISR (through getStaticProps), creating a unique cache key of our post.

You can name your key however you wish. In this case, the key matches an /api route for consistency's sake; e.g. for other pages that might require SWR's client-side fetching.

// pages/post/[id]
import useSWR from "swr";
import { fetcher } from "@/utils/fetcher";

export const getStaticPaths = async () => {
  // …custom logic to create paths for each `id`

export const getStaticProps = async () => {
  // …custom logic to populate `id` and `initialData`

const PostPage = ({ id, initialData }) => {
  // useSWR will:
  // 1. Create a cache key for the post
  // 2. Use the `initialData` and **won't** trigger a fetch
  const { data, error } = useSWR("/api/post" + id, fetcher, { initialData });

  const post = !error && data?.post;

  return (

export default PostPage;

On the "edit" page, we can mutate the post's unique cache key with any updated data. On redirect back to the post page, the user will see their updated post instantly, with ISR triggered in the background for the next user.

// pages/post/edit/[id]
import { useRouter } from "next/router";
import useSWR from "swr";
import { fetcher } from "@/utils/fetcher";

const PostEditPage = ({ id, initialData }) => {
  const router = useRouter();
  const { mutate } = useSWR("/api/post" + id, fetcher, { initialData });

  const handleSubmit = (newData) => {
    // mutate the post however you wish, in this case prepend to other posts.
      "/api/post" + id,
      (prevData) => ({
        post: {
      // Disable revalidation

    // Prevent the user from navigating `back` to this page

  return <form>{/* Form Logic */}</form>;

export default PostEditPage;

When used together, ISR and SWR offer lightning-fast static pages, instantly updated for the current user and statically regenerated for the next.

Visit the demo to see this in action…

…alternatively, join the Give&Bake beta waitlist to try the recipe pages first-hand.

––– views