Embed Sanity Studio in Next.js
Mount Sanity Studio as a route in your Next.js application using NextStudio. Covers auto and manual installation, App Router catch-all route, metadata, and route separation.
Sanity Studio can run as a route inside your Next.js application, giving you a single deployment for both your frontend and content editing interface. Mounting the studio at a route like /studio means shared authentication context, one build pipeline, and the ability to dynamically configure the studio based on environment or user context.
Consider your project size
Co-locating the studio with your Next.js app is convenient for small to medium projects. For larger projects and teams, a standalone studio or monorepo setup prevents your content model from becoming too website-centric and makes collaboration easier.
Prerequisites
- An existing Next.js 16+ project using App Router. If you're starting from scratch, follow the
create-next-appsetup first. - Node.js 22.12 or later.
- A Sanity account and project. If you don't have one, the automatic installation flow creates one for you.
next-sanityinstalled in your project.
Automatic installation (recommended)
The fastest way to embed the studio:
npx sanity@latest initpnpm dlx sanity@latest inityarn dlx sanity@latest initbunx sanity@latest initRun this inside your existing Next.js project. The CLI detects your framework, then:
- Prompts you to create or connect a Sanity project.
- Prompts you to choose a studio route (default:
/studio). - Writes the configuration files (
sanity.config.ts,sanity.cli.ts, and the route file). - Adds
http://localhost:3000to your project's CORS origins, if it isn't there already.
If you already have a Sanity project, the CLI connects to it. If not, it creates one.
Manual installation
Install peer dependencies
sanity and styled-components are peer dependencies required for the studio. Most package managers (npm v7+, pnpm v8+) install them automatically when you install next-sanity.
If you use yarn v1, run the following command:
npx install-peerdeps --yarn next-sanitypnpm dlx install-peerdeps --yarn next-sanityyarn dlx install-peerdeps --yarn next-sanitybunx install-peerdeps --yarn next-sanityCreate the sanity.config.ts file
Create a studio configuration file at the root of your Next.js project:
// sanity.config.ts
'use client'
import { defineConfig } from 'sanity'
import { structureTool } from 'sanity/structure'
export default defineConfig({
basePath: '/studio',
projectId: process.env.NEXT_PUBLIC_SANITY_PROJECT_ID!,
dataset: process.env.NEXT_PUBLIC_SANITY_DATASET!,
plugins: [structureTool()],
schema: { types: [] },
})A few things to note about this configuration:
'use client'is required. Sanity Studio is a client-side React application.basePathmust match the route where you mount the studio.schema.typesis where you define your content model. Start empty and add types as you build.
Create the sanity.cli.ts file (optional)
This enables npx sanity commands from within your Next.js project:
// sanity.cli.ts
import { defineCliConfig } from 'sanity/cli'
export default defineCliConfig({
api: {
projectId: process.env.NEXT_PUBLIC_SANITY_PROJECT_ID!,
dataset: process.env.NEXT_PUBLIC_SANITY_DATASET!,
},
})With this in place, you can run commands like npx sanity cors add or npx sanity schema extract directly from your project.
Create the studio route
Create an App Router catch-all route for the studio:
// src/app/studio/[[...tool]]/page.tsx
import { NextStudio } from 'next-sanity/studio'
import config from '../../../../sanity.config'
export const dynamic = 'force-static'
export { metadata, viewport } from 'next-sanity/studio'
export default function StudioPage() {
return <NextStudio config={config} />
}How this works:
- [[...tool]] is an optional catch-all segment that captures all studio sub-routes, including structure, vision, and media.
- dynamic = 'force-static' tells Next.js to statically render this route. The studio shell (HTML, CSS, JavaScript) is the same for all users. Only the data inside is dynamic, loaded client-side after the shell renders. Static rendering gives the fastest initial load.
- metadata and viewport exports from
next-sanity/studioset mobile-friendly viewport settings and prevent search engines from indexing the studio route. - NextStudio wraps Sanity Studio in a full-height layout and shows a fallback message when JavaScript is disabled.
Customize metadata
The default metadata exports handle the common case. To customize them, replace the export { metadata, viewport } from 'next-sanity/studio' line in the route file with your own exports:
// src/app/studio/[[...tool]]/page.tsx
import type { Metadata, Viewport } from 'next'
import {
metadata as studioMetadata,
viewport as studioViewport,
} from 'next-sanity/studio'
export const metadata: Metadata = {
...studioMetadata,
title: 'Loading Studio...',
}
export const viewport: Viewport = {
...studioViewport,
interactiveWidget: 'resizes-content',
}Spread the defaults and override specific fields. The defaults include robots: 'noindex' and mobile viewport settings that you'll want to keep.
Add custom components around the studio
If you need to inject custom navigation, wrap the studio in additional context providers, or access studio hooks outside of plugins, you can use StudioProvider and StudioLayout as children of NextStudio:
// src/app/studio/[[...tool]]/CustomStudio.tsx
'use client'
import { NextStudio } from 'next-sanity/studio'
import { StudioProvider, StudioLayout } from 'sanity'
import config from '../../../../sanity.config'
export function CustomStudio() {
return (
<NextStudio config={config}>
<StudioProvider config={config}>
{/* Components here have access to the same React hooks as Studio plugins */}
<StudioLayout />
</StudioProvider>
</NextStudio>
)
}// src/app/studio/[[...tool]]/page.tsx
import { CustomStudio } from './CustomStudio'
export const dynamic = 'force-static'
export { metadata, viewport } from 'next-sanity/studio'
export default function StudioPage() {
return <CustomStudio />
}How this works: NextStudio handles the layout around the studio (a full-height container and a fallback message when JavaScript is disabled). When you pass children, it delegates rendering to them instead of its default StudioProvider/StudioLayout. This lets you inject custom components between the provider and layout while keeping the Next.js wrapper benefits. The custom tree lives in its own client component, so page.tsx can stay a server component and keep the metadata and viewport exports.
Most applications don't need this level of control. Use the standard <NextStudio config={config} /> pattern unless you need to:
- Inject custom navigation or toolbar components
- Access studio React hooks outside of plugins
- Wrap the studio in additional context providers
Pages Router projects
Even if the rest of your application uses Pages Router, embed the studio on an App Router route. Next.js supports both routers in the same application. The studio is a client-side React app that works best with App Router's layout system, and the NextStudio component is designed for App Router.
Your file structure looks like this:
src/
app/
layout.tsx # Root layout (required by App Router)
studio/
[[...tool]]/
page.tsx # Studio (App Router)
pages/
index.tsx # Your site (Pages Router)
posts/
[slug].tsxKeep SanityLive and VisualEditing off the studio route
If you're using <SanityLive /> and <VisualEditing /> in your root layout, make sure they don't render on the Studio route. These components are for your content pages, not the Studio itself.
Use route groups to separate layouts:
src/app/
(app)/ # content routes
layout.tsx # includes <SanityLive /> and <VisualEditing />
page.tsx
posts/
[slug]/
page.tsx
(studio)/ # Studio route
layout.tsx # root layout, no SanityLive, no VisualEditing
studio/
[[...tool]]/
page.tsx # no SanityLive, no VisualEditingThe (app) and (studio) groups create separate layout trees. The studio route gets its own layout without the Live Content and Visual Editing components. Each group needs its own root layout, so move your existing src/app/layout.tsx into (app) rather than keeping it above both groups. The studio route file also sits one folder deeper, so its config import becomes ../../../../../sanity.config.
Troubleshooting
The studio route fails with Export default doesn't exist in target module
Make sure sanity.config.ts includes 'use client' at the top. Without this directive, Next.js tries to render the studio configuration on the server, which fails because the studio is a client-side application.
Studio loads but the URL doesn't match
The basePath in sanity.config.ts must match the actual route where the studio is mounted. If your studio route is /admin instead of /studio, update basePath to '/admin'.
Peer dependency errors during installation
If your package manager reports missing peer dependencies for sanity or styled-components, install them explicitly. This is common with yarn v1, which does not auto-install peer dependencies. Run npx install-peerdeps --yarn next-sanity or install each package manually.
Next steps
- Visual Editing with Next.js App Router: set up Visual Editing and live preview.
- Work-ready Next.js: Sanity Learn's multi-course track to level up your Next.js + Sanity skills.
Was this page helpful?
Feedback submitted, thanks for helping us improve the docs. Have you tried these alternatives?