NextJS App Router Migration with Sanity - Part 1
Learn how we migrated our NextJS Sanity app to the App Router
Introduction:
If you’re like many other app owners out there, you’ve probably waited until recently to start migrating your NextJS app from the Pages Router to the new App Router. Maybe you’re on the fence about migrating to the App Router because you’ve heard of others reporting bugs regarding “On-Demand” Revalidation. Or you’ve just been putting it off because of the amount of effort and time it would take for your team to migrate your app. Operation Nation recently decided it was time to finally migrate our NextJS Sanity App over to the App Router. We decided this for a few reasons. First, is because NextJS announced it reached a “stable” version. Second, they state in their docs that they recommend anyone building a new NextJS app should opt for the App Router. And third, we wanted to leverage React Server Components and improve the speed and performance of the websites we build for our clients.
Operation Nation maintains our own internal NextJS Sanity starter kit called the “ONE Builder”. It comes with all the components, modules, plugins, and infrastructure that any top-tier website would need for production. Recently, we just finished phase 1 of upgrading the ONE Builder and we want to share our process and steps with you so you can more easily migrate your own NextJS and Sanity application to the App Router.
What is Phase 2 of the migration you might ask? Good question. We use ChakraUI for our styles and component library. As you probably already know, Chakra is built on top of Emotion and compiles its styles during run time. This requires you to utilize use client in any component in your app that uses a Chakra component. This isn’t ideal with the App router as it negatively impacts performance. So, in Part 2 of this migration guide, we’ll go over how we migrated our app from ChakraUI to ParkUI.
Without going into too much detail, ParkUI is built on top of ArkUI and PandaCSS. This tool set is built by the creators and contributors of ChakraUI. It’s a solution that allows you to keep using the familiar syntax of styled-system that we all know and love in ChakraUI. PandaCSS is a CSS-in-JS with build time generated styles, RSC compatible, multi-variant support, and best-in-class developer experience.
So without further ado, let’s dive into Phase 1 of the migration: Pages -> App Router
App Router Migration Steps:
The migration from the Pages to the App Router can include a lot of refactoring. It's a complete paradigm shift to using React Server Components. Luckily, the NextJS team has made it so you can do an incremental migration. The Pages and App Router directories can exist alongside each other.
I’m not going to go into great detail about each migration step because the NextJS Docs do an excellent job of outlining all the steps. So the following steps in this article will cover what the significant tasks were in the context of our app using Sanity.
The migration steps can be categorized into a couple of different sections. First, you need to complete all the steps that would be required regardless of whether you are using Sanity or not. Next, is updating all of your Sanity related code when it comes to using the official Sanity NextJS tool kit next-sanity
1: Update NextJS, React, ESLint, and Node
Create the new /app directory and update to the latest NextJS and React versions. You must use a minimum Node.js version of v18.17.0.
Upgrade ESLint version
2: Migrate Internationalization
Yes, our NextJS Sanity Kit has localization built right in. This is one of the reasons we had to wait so long to migrate. With the release of the new App Router, NextJS has removed internationalized routing as a built-in feature 😔. So, we had to wait until there was another solution available. We ended up using the next-i18n-router to have internationalized routing.
Installation:
First, we nested all pages and layouts inside of a dynamic segment named [locale] in the app dir:
Then we created a file called i18nConfig.ts at the root of our project to store our config:
Then we updated our middleware.ts file at the root of our project where the i18nRouter will be used to provide internationalized redirects and rewrites:
We also created a handy hook to make the variables reusable in @utils/useLocale.ts
3: Install and Implement Google Tag Manager
Install @next/third-parties to integrate third-party libraries such as GTM(Google Tag Manager, Map, Youtube, etc). Full instructions for this package can be found on NextJS Docs.
4: Create Root Layout File
Create a layout.tsx in the /app directory. The Root layout file is where we initialize Google Tag Manager and use generateStaticParams to build our pages with all the configured locales.
5: Migrate Pages and Create Root Home Page
Here are some things to keep in mind as you’re migrating your pages.
- Pages in the
/appdirectory are Server Components by default. This is different from the Pages router because in there, all the pages were Client Components. - The
/appdirectory uses nested folders to define routes and a specialpage.jsfile to make a route segment publically accessible. - Keep your data fetching in the Server components and any client interactions in your Client components.
6. Migrate next/head with Dynamic Metadata Function
In the App Router, next/head package is no longer used. It’s been replaced with Metadata component.
Usage:
7: Create Dynamic Page Component
Next, you can create another page component in app/[slug]/page.tsx . This time we have passed slug as params. Following this structure, you can create any nested routes in the app dir.
The generateStaticSlugs function is what’s used to generate static paths for the dynamic page.
8. Create client.ts and token.ts in lib folder
9. Embedding Sanity Studio
Install or update the next-sanity package. This is Sanity’s official toolkit for building NextJS applications with Sanity.
Create a folder called studio in the root of the app directory /app/studio
then add another folder called [[...index]] inside the studio
Then create page.tsx and Studio.tsx component
10. On-Demand Revalidation with tags and webhook
ISR (Incremental Static Revalidation) in the Pages Router allows you to update static pages on a per-page basis without the need to rebuild your entire site. The equivalent of that in the App Router is on-demand revalidation with tags, paths, or time-based methods.
The next-sanity toolkit fully supports the NextJS revalidation features. Tag-based validation gives you fine-grained control for when you want to revalidate content
Note: Remember to set up a webhook in Sanity
We implement a loadQuery function to handle fetching data with tagRevalidation.
Usage:
Now, for the revalidateTag to work properly you have to set up an API route in your NextJS app that handles incoming requests, typically made by a GROQ Powered Webhook.
“The code example below uses the built-in parseBody function to validate that the request comes from your Sanity project (using a shared secret + looking at the request headers). Then it looks at the document type information in the webhook payload and matches that against the revalidation tags in your app:”
If you still have questions about implementing tag revalidation with NextJS and Sanity, read this in-depth article by Rudderstack.
Wrapping Up:
Updating from the Pages to the App Router in NextJS wasn't too painful. Tools like the Sanity NextJS toolkit make it easier and provide great documentation on how to do it.
The fact that you can do it incrementally while the Pages and App Router live side by side also makes it less of a daunting task so you don’t have to do it all in 1 sprint.
Since we were using ChakraUI for our component library and it’s not designed to work with React Server Components, we took quite a performance hit. Almost every front-end file has to utilize use client because of Chakra. But stay tuned, because phase 2 of our migration will cover our migration from ChakraUI to ParkUI!
If your website is a NextJS app using the Pages Router and you want to migrate over to the App Router but don’t have the time or resources to do it yourself, reach out and see how we can help! Thanks for reading 🍻

Eric Nation
|Co-Founder & Head of Engineering
Eric Nation, co-founder and partner at Operation Nation, spearheads the Product and Web Development division. Boasting over a decade of expertise as a professional software engineer, Eric thrives on crafting immersive web applications and sites. Away from the keyboard, you can find him at the gym, surfing, skateboarding, or on a motorcycle adventure somewhere.
