Sanity

How to Use Sanity Typegen for Type-Safe Content Queries

Learn how Sanity typegen automatically generates TypeScript types from your schema, enabling fully type-safe GROQ queries and eliminating runtime content errors.

June 26, 202611 min readMuhammad Zohaib Ramzan
TypeScript code editor showing type-safe content queries with Sanity Typegen, featuring colorful syntax highlighting on a dark background

If you've ever shipped a content change only to discover a broken page in production, you know the pain of untyped content queries. Sanity typegen solves this problem by automatically generating TypeScript types directly from your Sanity schema — giving you end-to-end type safety from your content model all the way to your React components. In this tutorial, you'll learn exactly how to set it up, run it, and make the most of it in a real Next.js project.

What is Sanity Typegen

Sanity typegen is a code-generation tool built into the Sanity CLI that introspects your project's schema and produces TypeScript type definitions for every document type, object type, and GROQ query result in your codebase. Instead of manually maintaining interface Post { title: string; ... } definitions that drift out of sync with your actual schema, typegen keeps your types accurate and up to date automatically.

The tool works in two stages. First, it extracts a JSON schema representation of your Sanity content model. Second, it statically analyses your source files for GROQ query strings and generates precise return-type definitions for each one. The result is a single generated file — typically sanity.types.ts — that you import throughout your project.

Key benefits include:

  • Catch content errors at compile time, not at runtime
  • Autocomplete for document fields in your editor
  • Refactor safely — rename a schema field and TypeScript immediately flags every affected query
  • Zero runtime overhead — types are erased at build time

Setting up Typegen in a Next.js Project

Before you begin, make sure you have the Sanity CLI installed and a Sanity project initialised inside (or alongside) your Next.js app. Install the CLI as a dev dependency:

npm install --save-dev @sanity/cli

Next, ensure your sanity.config.ts is present at the root of your project and exports a valid configuration object:

// sanity.config.ts
import { defineConfig } from 'sanity'
import { deskTool } from 'sanity/desk'
import { schemaTypes } from './schemas'

export default defineConfig({
name: 'default',
title: 'My Blog',
projectId: process.env.NEXT_PUBLIC_SANITY_PROJECT_ID!,
dataset: process.env.NEXT_PUBLIC_SANITY_DATASET!,
plugins: [deskTool()],
schema: { types: schemaTypes },
})

Then add a sanity.cli.ts file at the project root so the CLI knows where to find your config:

// 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!,
},
})

Finally, add a typegen script to your package.json: "typegen": "sanity typegen generate".

Running Typegen to Generate Types

With the configuration in place, generating types is a single command:

npx sanity typegen generate

The CLI will:

  1. Read your sanity.config.ts to extract the full schema
  2. Scan your source files for GROQ query strings tagged with defineQuery or assigned to typed variables
  3. Output a sanity.types.ts file at the project root

You should commit sanity.types.ts to version control so that CI and your teammates always have the latest types. To keep types fresh during development, run typegen in watch mode: npx sanity typegen generate --watch.

Writing Type-Safe GROQ Queries

To get the most out of typegen, wrap your GROQ queries with the defineQuery helper from next-sanity. This tells the CLI exactly which strings to analyse. For example, in lib/queries.ts:

import { defineQuery } from 'next-sanity'

export const allPostsQuery = defineQuery(`
*[_type == "post"] | order(publishedAt desc) {
_id, title, slug, publishedAt,
"authorName": author->name
}
`)

After running npx sanity typegen generate, the CLI produces a matching type in sanity.types.ts — for example, AllPostsQueryResult typed as an array with _id, title, slug, publishedAt, and authorName fields, each correctly typed as nullable where appropriate. You can then import and use this type anywhere in your project, confident it always reflects the real query shape.

Using Generated Types in React Components

Import the generated type alongside your query and pass it through your data-fetching layer. Here's a complete Next.js App Router page:

// app/blog/page.tsx
import { client } from '@/lib/sanity.client'
import { allPostsQuery } from '@/lib/queries'
import type { AllPostsQueryResult } from '@/sanity.types'

export default async function BlogPage() {
const posts: AllPostsQueryResult = await client.fetch(allPostsQuery)
return (
<ul>
{posts.map((post) => (
<li key={post._id}>
<a href={`/blog/${post.slug?.current}`}>{post.title}</a>
</li>
))}
</ul>
)
}

Because AllPostsQueryResult is derived directly from your GROQ projection, TypeScript knows that post.slug is nullable and will warn you if you forget to handle that case. For reusable components, derive the item type using AllPostsQueryResult[number] as the prop type — this avoids duplicating type definitions and stays in sync automatically.

Updating Types When Schema Changes

Whenever you add, rename, or remove a field in your Sanity schema, re-run npx sanity typegen generate to regenerate sanity.types.ts. TypeScript will immediately surface every location in your codebase that references the changed field. For example, if you rename publishedAt to publishDate in your schema, any component that still reads post.publishedAt will produce a TypeScript error, guiding you to update each reference before the bug ever reaches production.

For teams, add typegen to your CI pipeline to fail the build if sanity.types.ts is out of sync with the current schema:

npx sanity typegen generate
git diff --exit-code sanity.types.ts

This fails the build if the generated file has drifted, enforcing that types are always committed and up to date.

Common Mistakes

Avoid these pitfalls when working with Sanity typegen:

  • Not using defineQuery — Bare template literals or plain strings are not analysed by the CLI. Always wrap queries with defineQuery to get generated return types.
  • Editing sanity.types.ts manually — The file is overwritten on every run. Put any custom type utilities in a separate file.
  • Forgetting to re-run after schema changes — Stale types are worse than no types because they give false confidence. Automate regeneration in CI.
  • Projecting fields that don't exist in the schema — Typegen will type the result as null or undefined, which can cause subtle bugs if you assume the field is always present.
  • Using any to silence type errors — This defeats the purpose of typegen. Fix the underlying query or schema mismatch instead.
  • Ignoring nullable fields — Many Sanity fields are optional. Typegen correctly marks them as T | null; handle these cases explicitly.
  • Running typegen against the wrong dataset — If your SANITY_DATASET env variable points to a staging dataset with a different schema, your generated types may not match production.

Best Practices

Follow these guidelines to get the most out of Sanity typegen:

  • Co-locate queries with the components that use them, or centralise them in a lib/queries.ts file. Either way, keep them consistent so typegen can find them all.
  • Use defineQuery for every GROQ string — even simple ones. Consistency makes the codebase easier to audit.
  • Commit sanity.types.ts to version control — treat it like a lock file: generated, committed, and reviewed in pull requests.
  • Add typegen to your CI pipeline — fail the build if the generated file is out of date.
  • Prefer narrow projections — only select the fields you actually need. This keeps generated types small and your queries fast.
  • Use AllPostsQueryResult[number] to derive item types from array results rather than duplicating type definitions.
  • Enable strict mode in tsconfig.json — typegen's nullable fields are only useful if TypeScript is configured to enforce null checks.

FAQ

Does Sanity typegen work with the App Router in Next.js 13+? Yes. Typegen is framework-agnostic — it generates plain TypeScript types that work in any environment, including React Server Components, the Next.js App Router, and Pages Router alike.

Do I need to run typegen every time I save a schema file? Not necessarily during active development, but you should run it before committing schema changes. The --watch flag (sanity typegen generate --watch) can automate this during local development sessions.

What happens if my GROQ query is dynamic (built at runtime)? Typegen performs static analysis, so it cannot analyse queries constructed dynamically at runtime. For dynamic queries, you'll need to manually define the expected return type or use a generic helper. Where possible, prefer static query strings.

Can I use typegen with a Sanity project that has multiple datasets? Yes, but you'll need to run typegen once per dataset configuration and manage the resulting type files separately. Most teams use a single canonical dataset (usually production) as the source of truth for type generation.

Is sanity.types.ts safe to import in both server and client components? Absolutely. The generated file contains only TypeScript type and interface declarations, which are erased at compile time and produce zero JavaScript output. There is no runtime cost to importing from it.

Conclusion

Sanity typegen is one of the most impactful developer-experience improvements you can add to a Sanity-powered project. By automatically generating TypeScript types from your schema and GROQ queries, it closes the gap between your content model and your application code — catching errors at compile time, enabling rich editor autocomplete, and making schema refactors safe and straightforward.

Start by installing the Sanity CLI, wrapping your queries with defineQuery, and running npx sanity typegen generate. Commit the output, wire it into CI, and you'll have a content layer that's as type-safe as the rest of your TypeScript codebase. Give it a try on your next project — once you've experienced autocomplete on your Sanity document fields, you won't want to go back.