Build Static Pages with Strapi in Next.js

Combining Next.js with a headless CMS like Strapi offers a powerful solution for building high-performance, content-driven websites. One of the most effective strategies for performance and SEO is to generate static pages at build time—but what happens when content changes after deployment?

We can use Next’s Incremental Static Regeneration (ISR) with Strapi’s webhooks to revalidate our static pages. Here’s how you can use them together to create a smooth publishing workflow.

Note: I’ll be using the Next.js Pages router in this post. Personally, I believe it provides a clearer API when working with static pages and a headless CMS. However, the essence of this webhook/static regeneration technique is the same with the App router using generateStaticParams.

The Setup: Static Pages with Strapi and Next.js

Let’s say you’re building a blog. Your content lives in Strapi, and you want each blog post to be a static page generated by Next.js.

In getStaticPaths, you fetch all blog post slugs:

export async function getStaticPaths() {
  const res = await fetch('https://your-strapi-api.com/api/posts');
  const posts = await res.json();

  const paths = posts.data.map((post) => ({
    params: { slug: post.attributes.slug },
  }));

  return { paths, fallback: 'blocking' };
}

Then, in getStaticProps, you fetch the post data for each page:

export async function getStaticProps({ params }) {
  const res = await fetch(`https://your-strapi-api.com/api/posts?filters[slug][$eq]=${params.slug}&populate=*`);
  const post = await res.json();

  if (!post.data.length) return { notFound: true };

  return {
    props: { post: post.data[0] },
    revalidate: 60, // Enable ISR: this page will regenerate at most once per minute
  };
}

With this setup, each post page is statically generated but can be updated incrementally with ISR. Since we set the revalidate time to 60, the static page will be revalidated by running getStaticProps again every minute.

Handling Real-Time Updates with a Webhook

Now, using a time period to revalidate your page isn’t the most performant solution in this situation, because we really only need to revalidate the page when we make a content change in Strapi. Ideally, we want our updates from Strapi to trigger a static page regeneration for the page that’s updated. We can handle this on-demand revalidation method by creating a Strapi webhook and a custom Next API route.

In Next.js, let’s expose an API route by creating a file like pages/api/revalidate/index.js:

export default async function handler(req, res) {
  // Basic security check (e.g. secret token from Strapi)
  if (req.headers.authorization !== process.env.REVALIDATE_SECRET) {
    return res.status(401).json({ message: 'Invalid token' });
  }

  const event = req.body;

  if (event.model === 'post') {
    await res.revalidate(`/blog/${event.entry.attributes.slug}`);
  }

  const slug = event?.entry?.slug;
  if (!slug) {
    return res.status(400).json({ message: 'Missing slug' });
  }

  try {
    // Revalidate the specific blog post page
    await res.revalidate(`/blog/${slug}`);
    return res.json({ revalidated: true });
  } catch (err) {
    return res.status(500).send('Error revalidating');
  }
}

This will expose an endpoint at /api/revalidate at which we can send our Strapi webhook events to. A crucial part of this step is determining which static page to revalidate based on which Strapi collection is published. Our Strapi webhook will send a request with an event payload containing the collection entry we hit publish on. Above, I used this event payload to specify which static slug should be revalidated by using the entry’s post slug. You can check out the schema for what this payload looks like here.

 Next, let’s create the Strapi webhook. In Strapi, you can create a webhook by navigating to Settings -> Webhooks.

Create a webhook to hit the API endpoint you created above, using the auth token you created as well. We really only need to select the “publish” event here, but you can utilize the other events as well (check out the Strapi docs for more info on these).

Save your new webhook, and that’s it! When you hit “publish” on a collection in Strapi (say, a new blog post), it will send an event to your custom API endpoint. Your static page will be revalidated and retrieve the latest updated data from Strapi.

Conclusion

By combining Next.js static site generation, Incremental Static Regeneration, and a Strapi webhook, you get the best of both worlds:

  • Speed: Static pages are lightning-fast.

  • Freshness: Pages update in near real-time.

  • Control: You decide when and how content gets regenerated.

Use this setup for blogs, documentation sites, marketing pages, or any content-heavy project where performance and dynamic updates are both top priorities. Of course, be sure to utilize the Next.js docs and the Strapi docs to fine tune your setup. Happy coding!

Conversation

Join the conversation

Your email address will not be published. Required fields are marked *