
Next.js 15 Sanity CMS Setup Guide With GROQ & Vercel
Introduction
Building modern web applications requires a delicate balance between blazing-fast frontend performance and flexible content management. When I look at production web experiences, I look for clean, type-safe patterns that keep maintenance low and developer velocity high. Combining the Next.js 15 App Router with Sanity CMS gives you powerful server-side rendering, instant content updates, and structured content schemas right out of the box. If you want to publish dynamic blog posts or power a scalable product catalog without wrestling clunky boilerplate, this setup blueprint walks you through every single step.

Initializing Your Next.js 15 App Router Project
Starting a new project requires clean directory organization and strict TypeScript support from day one. Using create-next-app with Next.js 15 sets up the modern App Router architecture, enabling Turbopack for lightning-fast local builds and instant server refreshes. Make sure you configure your environment variables and define clean path aliases early in your tsconfig.json. When you run your initialization command, you can set up your project structure quickly with this command:
npx create-next-app@latest my-app --typescript --tailwind --eslint --app --src-dir --import-alias "@/*"
Proper environment configuration ensures your API keys and project identifiers remain secure while allowing your development server to communicate with external data sources. For founders building complex platforms, reviewing professional CMS & E-commerce Development practices ensures your project structure remains maintainable as your team grows.
- What environment flags ensure smooth local execution?
- Should you choose TypeScript from day one?
Setting Up Sanity Studio and Schemas
Sanity CMS gives content creators full control over structured content while keeping developer friction exceptionally low. Initializing Sanity directly inside your App Router directory creates an embedded studio route at /studio. You can install the official Sanity CLI and initialize your workspace by running these commands:
npm install sanity@latest next-sanity@latest @sanity/image-url@latest
npx sanity init --env
Once initialized, define your document schemas for authors, blog posts, or products in dedicated TypeScript files within your sanity folder. Here is a sample author schema definition:
export const author = {
name: 'author',
title: 'Author',
type: 'document',
fields: [
{
name: 'name',
title: 'Name',
type: 'string',
},
{
name: 'slug',
title: 'Slug',
type: 'slug',
options: {
source: 'name',
maxLength: 96,
},
},
{
name: 'image',
title: 'Image',
type: 'image',
options: {
hotspot: true,
},
},
],
}
This setup keeps your content schemas type-safe and clear across your entire application. If you are planning a full-stack product, checking Next.js SaaS Development workflows can help you plan scalable data schemas before writing code.
How can embedded studio routes simplify your content editing workflow? An embedded route keeps your editorial team inside your primary domain, avoiding separate admin portals and simplifying authentication.
- How can embedded studio routes simplify your content editing workflow?
- Which field types best suit your custom content model?
Configuring the Sanity Client and Fetching Data
Connecting your frontend to Sanity requires configuring next-sanity with your project ID, dataset, and API version. Fetching data with GROQ queries inside Next.js 15 Server Components keeps API keys secure and optimizes initial page loads. Create a client configuration file to manage your API connections:
import { createClient } from 'next-sanity'
export const client = createClient({
projectId: process.env.NEXT_PUBLIC_SANITY_PROJECT_ID,
dataset: process.env.NEXT_PUBLIC_SANITY_DATASET || 'production',
apiVersion: '2024-03-01',
useCdn: process.env.NODE_ENV === 'production',
})
Next, use GROQ queries to fetch your posts inside your Server Components. GROQ lets you filter, project, and shape your JSON data directly on the server before sending HTML to the client:
import { client } from '@/sanity/client'
const POSTS_QUERY = `*[_type == "post" && defined(slug.current)]|order(publishedAt desc)[0...12]{
_id,
title,
slug,
publishedAt
}`
export async function getPosts() {
return await client.fetch(POSTS_QUERY)
}
You can also configure remote image domains in next.config.js to serve optimized images directly through Sanity CDN:
const nextConfig = {
images: {
remotePatterns: [
{
protocol: 'https',
hostname: 'cdn.sanity.io',
},
],
},
};
module.exports = nextConfig;
Why should you use GROQ queries inside Server Components? GROQ lets you filter, project, and shape your JSON data directly on the server before sending HTML to the client.
- Why should you use GROQ queries inside Server Components?
- How do remote image domains improve page performance?
Configuring Live Preview and Visual Editing
Live preview allows content editors to review draft changes before publishing live updates. Setting up Draft Mode using Next.js route handlers enables real-time visual editing directly inside Sanity Studio. Create a route handler at app/api/draft-mode/enable/route.ts to handle incoming preview tokens securely:
import { validatePreviewUrl } from '@sanity/preview-url-secret'
import { draftMode } from 'next/headers'
import { redirect } from 'next/navigation'
import { client } from '@/sanity/client'
export async function GET(request: Request) {
const { isValid, redirectTo = '/' } = await validatePreviewUrl(
client,
request.url
)
if (!isValid) {
return new Response('Invalid secret', { status: 401 })
}
draftMode().enable()
redirect(redirectTo)
}
This eliminates guesswork for non-technical team members editing page layouts or promotional banners. Maintaining clear draft state management keeps published content safe while giving creators instant visual feedback.

Is live draft mode necessary for your content team? Draft mode prevents accidental publication of unfinished work while letting editors inspect changes in real time.
- Is live draft mode necessary for your content team?
- How does real-time preview improve publishing confidence?
Deploying Next.js 15 and Sanity to Production
Deploying your setup to Vercel requires defining production environment variables for your Sanity dataset and API tokens. Adding CORS origins inside your Sanity management dashboard ensures your deployed frontend can communicate securely with the content studio. Automated build pipelines trigger fresh dynamic generation or static revalidation whenever content changes.
Here is a quick checklist for your production deployment:
- Add
NEXT_PUBLIC_SANITY_PROJECT_IDandNEXT_PUBLIC_SANITY_DATASETto your Vercel project settings. - Configure your production domain inside your Sanity account settings under API CORS origins.
- Set up webhook triggers to revalidate paths automatically when documents update.
Which environment variables must remain secret in production? Your Sanity write tokens and private API keys must remain strictly protected as server-side environment variables.
- Which environment variables must remain secret in production?
- How do CORS configurations protect your dataset?
Follow Owais Abdullah on Google Search & Discover
Add this domain as a preferred source to see new AI engineering, Next.js SaaS, and Digital FTE breakdowns prioritized in your Google Top Stories, AI Overviews, and Discover feed.

Owais Abdullah
Web & AI Engineer · Founder @ Octively
Spec-driven developer and AI engineer. Founder of Octively, building Next.js SaaS platforms, autonomous Digital FTEs (AI employees), and production-ready intelligent workflows.
Recent Posts
Did you find this article helpful?
Questions I get
Frequently Asked Questions
Discussion & Thoughts
Join the conversation with your perspective



