Easing into the App Router with the Sanity Toolkit for Next.js (ssr)

Written by Marissa Ghassemian, Knut Melvær

Missing Image!

Picture this: you're on the hunt for information or looking to make a purchase online, but the webpage you clicked is taking an eternity to load. Frustrating, right? How often do you just bail when this happens? We all know these experiences drive users away, but let’s face it: it takes a lot of time and skill to develop a performant site, especially for the dynamic, rich experiences that our visitors love. It’s also worth it: websites that meet user expectations and needs instill a sense of trust and reliability in a brand, make it easier for users to achieve their online objectives, and ultimately help you grow your business.

Today we’re happy to share our latest improvements to Sanity’s Next.js toolkit, which combines the might of our client library,@sanity/client, and Next.js, all in one convenient package. Together with Next.js App Router, these improvements reduce the burden on developers to build the complex caching infrastructures needed to power fast, fresh content loads while letting you remove lines of code, perform less maintenance and make fewer API calls.

More delightful = more demanding

Brands that win are constantly launching richer, more dynamic experiences that stand out. The teams that make these experiences a reality are faced with major challenges as they need to maintain performance as sites become more:

Overcoming performance drains with Sanity and Vercel

Staying ahead of your competitors requires harnessing the power of innovative tools and platforms. Vercel, a cloud platform for building, deploying, and scaling apps and websites, has been leading the charge in helping development teams meet the ever-increasing demands of their sites. They’re also the creator and maintainer of Next.js, one of the most popular web frameworks on the planet right now for React. Sanity, the Composable Content Cloud, offers the most control and customization of any CMS. Together, the Next.js, Vercel, and Sanity stack is unstoppable.

Sanity has improved how we build with content for our clients. We can give them bespoke content workspaces that make sense for their business, while enjoying the great developer experience that comes with Sanity Content Lake. With the new App Router, we have even more flexibility building successful user experiences, while ensuring great performance. Content creators can push content to production without having to wait for their changes to appear on the site, knowing that the site stays performant for their audience. It seems like this should be table stakes, but it can be really hard to do without the abstractions given to us by Sanity and Next.js.

Grant Sander, VP of Engineering, Formidable

PortableText [components.type] is missing "infoBox"

From static to dynamic

To address the need for a more scalable way to update content, Next.js released Incremental Static Regeneration (ISR) in 2021. With ISR, developers can use static-generation on a per-page basis (rather than having to rebuild an entire site for an isolated change). This has been a huge value for the developer community, but comes with challenges:

Now: App Router with React Server Components

Fast forward to 2023, Vercel’s latest release of Next.js 13 introduced App Router, giving you a deeper level of control to create sites that perform as fast as static sites, but are completely dynamic. How? By defining your caching strategy—dynamic vs. static—for individual components within a page, you avoid entire page reloads. This is powered by React Server Components, which makes every component on your website responsible for its own data. Using these new data fetching and caching paradigms, revalidation now moves to individual fetching.

The biggest mental model shift in Next.js 13 is to stop thinking which pages should be static or dynamic and start thinking about what data is static or dynamic. Moving the static and dynamic decisions to data fetching gives you more control over when and where to serve static or dynamic content.

Vercel

How exactly do you indicate what components need to be updated without revalidating the entire page? You can use revalidation tags. Revalidation tags mark a specific request correlated to the specified tag to be expired when information changes. This type of technology gives you the customizability to invalidate content with the granularity you see fit, for example, only this author, all authors, and so on. – the power is in your hands.

Sanity for the assist: Built-in compatibility for Next.js

Because Sanity treats content as data (structured and queryable) and offers a high degree of query flexibility with GROQ, it’s a well-suited headless CMS to pair with App Router. And we've fine-tuned our Next.js toolkit, next-sanity, to give developers a more intuitive and efficient experience as they build high-performance, user-delighting sites.

More for developers to love

Next.js is all about giving developers tooling to build as quickly as possible with minimal configurations. With the same intent, we’ve combined the power of our client library, @sanity/client, and Next.js, in one convenient package so developers can enjoy the DX of next-sanity and Next.js in one place. Benefits include:

Easing into App Router

React Server Components and the App Router represented a fundamental shift in how you build web applications with Next.js, and it can be daunting to have to re-learn a framework. Fortunately, Next.js lets you adopt the App Router incrementally alongside the Page Router. What’s more, our next-sanity toolkit is compatible with both! This means you get advanced caching and tag-based revalidation features to make your life a lot easier. In other words, using Next.js and Sanity together is a good way to get started with React Server Components, and start reaping its benefits.

Making Fetch happen

Fetch functionality in Next.js refers to the ability to retrieve data from an external source or API in a website. You can fetch data either at build time on the server or during runtime on the client, boosting not only your site's speed but also its SEO and overall user experience. In simple terms, it means quicker page loads, better user experience, better SEO rankings, less work for your client-side code, and even the ability to pre-render pages with ever-changing data. All leading to a smoother, faster, and more efficient website.

Our next-sanity toolkit is now fully compatible with Next.js’ capabilities, bringing together the best of its internal data-fetching methods and our own client library. This combo significantly reduces the number of API calls needed to keep content updated. Instead of constantly polling for changes, you can set up a webhook to trigger the appropriate calls only when a change occurs, eliminating the need for expiration checks. This makes it especially effective for e-commerce and media sites that require real-time updates and robust caching strategies.

While navigating caching options can often be complex and time-consuming, Sanity lets you easily opt for the most fitting caching approach for any page or component. And if your project demands more nuanced control—common in mature, dynamic sites—you can use revalidation tags for fine-grained management, making it easier to keep your content both fresh and efficient.

Example of a component with data fetching + revalidation tag

// app/components/TestimonialCard.tsx

async function FeaturedTestimonialCard() {
 const data = await sanityFetch({
   query: '*[_type == "testimonial" && featured == true][0]{name, title, photo}',
   // Will automatically trigger for when a webhook contains the `testimonial` type
   tags: ['testimonial'],
 )}

 return (
  <Card>
    <Image {...data.photo} />
    <Byline>{data.name}{title ? ` (${title})` : ''}</Byline>
  </Card>)
}

GROQ-Powered Webhooks + App Router

Revalidation tags and GROQ-Powered Webhooks make it much easier to hit the sweet spot between reducing API calls to Content Lake and never seeing stale content on your site. GROQ-powered Webhooks let you set up notifications to trigger (almost) any content change and let you freely build a payload based on that content – making it the most advanced content webhook feature in the industry. Companies that need content changes to publish quickly, like news and media, use GROQ-Powered Webhooks to monitor and respond to create, edit, and delete events.

To trigger an invalidation request, the URL/path-based approach has worked fine for simple content types like articles and posts. It has been harder to implement revalidation for more complex cases that are typical for modern web experiences. Let‘s say you have a document type for testimonials, that you use for different parts of your website. Before you had to figure out all the URLs that use the testimonial type to revalidate the cache for where it’s used. Using the tags-based approach, when you query Content Lake you can add a tag that says testimonial to the same function that fetches the data. Doing this allows Next.js to keep track of the data-fetching URLs that have asked for data and have the associated testimonial tag.

When a content change triggers a webhook, you can include data that tells Next.js which tag to revalidate. A simple example is to include the document _type in the webhook's payload, and use the _type name as the tag. In our example, the document type is testimonial, and when the webhook hits Next.js' serverless function, it matches that to the revalidate tags. Without the need to keep track of what URLs are impacted your code becomes significantly simpler to manage, compose, and comprehend.

API route for handling incoming GROQ-powered Webhooks that has {_type} as their body:

// app/api/revalidate/route.ts
/*
The relatively simple API route that handles the incoming request from a GROQ-Powered Webhook. 	

The webhook here could have the following settings:
      	  filter: `_type in ["testimonial", "page"]`
        projection: `{_type}`
        trigger on: [x] create [x] update [x] delete
*/

import {revalidateTag} from 'next/cache'
import {type NextRequest, NextResponse} from 'next/server'
import {parseBody} from 'next-sanity/webhook'

export async function POST(req: NextRequest) {
  try {
    const {isValidSignature, body} = await parseBody<{_type}>(
      req,
      process.env.SANITY_REVALIDATE_SECRET,
    )

    if (!isValidSignature) {
      const message = 'Invalid signature'
      return new Response(JSON.stringify({message, isValidSignature, body}), {status: 401})
    }

    if (!body?._type) {
      const message = 'Bad Request'
      return new Response({message, body}, {status: 400})
    }

    // If the `_type` is `testimonial`, then all `client.fetch` calls with
    // `{next: {tags: ['testimonial']}}` will be revalidated
    await revalidateTag(body._type)

    return NextResponse.json({body})
  } catch (err) {
    console.error(err)
    return new Response(err.message, {status: 500})
  }
}

Structured Content + App Router

Structured content is already a great way to update your content in a single place and have it be automatically updated everywhere it’s referenced without keeping track of URLs. Combining that with the power of Next.js’ cache and Vercel’s data layer will make the propagation of your content updates faster than ever – especially when combined with revalidation tags. Not only does this combining force help with the speed of updates, but it also comes at a much lower cost as structured content lets you know what parts of the cache don’t need to be revalidated. This means the longer the content lives in the cache the less API calls you need to make.

This is typically a difficult trade-off that an organization has to make – do they want to pay the price of high API usage rates to keep content fresh with changes going out quickly or do they want to keep costs low but have stale content on their site with updates being slowly rolled out. Revalidation tags are a great mechanism to get the best of the both worlds: maximum content reuse and content updates propagating without a delay.

Using App Router Effectively

App Router is key for any site built with Next.js and Sanity. However it’s particularly useful for:

Cutting edge capabilities for quick content updates

Using Vercel’s edge caching technology with Sanity’s unmatched content management capabilities users can redefine what it means to serve dynamic and static content. Combining the customization capabilities of structured content, GROQ-Powered webhooks, App Router, and revalidation tags your content is sure to deliver at the frequency specified by you. No matter how small or large the content update is, you’ll be sure to never show stale content again.

PortableText [components.type] is missing "infoBox"

Explore the power of Sanity's Next.js toolkit and elevate your web development today!

Install Next-Sanity