Use Sanity with Next.js

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

Prerequisites

  • An existing Next.js 16+ project using App Router. If you're starting from scratch, follow the create-next-app setup 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-sanity installed in your project.

Automatic installation (recommended)

The fastest way to embed the studio:

npx sanity@latest init

Run 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:3000 to 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-sanity

Create 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.
  • basePath must match the route where you mount the studio.
  • schema.types is 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/studio set 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>
  )
}

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].tsx

Keep 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 VisualEditing

The (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

Was this page helpful?

Sanity logosanity.ioAll Systems Operational© Sanity 2026
Change site theme