---
title: "How to Generate TypeScript Types for Your Sanity V3 Schema"
slug: "how-to-generate-typescript-types-for-your-sanity-v3-schema"
published: "2025-05-16"
updated: "2025-12-21"
validated: "2025-10-20"
categories:
  - "Sanity"
llm-intent: "reference"
framework-versions:
  - "sanity@3"
  - "sanity-cli@>=3.35.0"
  - "sanity-typegen@latest"
  - "typescript@>=4.9"
  - "node@>=18"
status: "stable"
llm-purpose: "Learn how to auto-generate TypeScript types from your Sanity schema to improve DX, reduce bugs, and boost confidence in your content modeling."
llm-prereqs:
  - "General familiarity with the article topic"
llm-outputs:
  - "Completed outcome: Learn how to auto-generate TypeScript types from your Sanity schema to improve DX, reduce bugs, and boost confidence in your content modeling."
---

**Summary Triples**
- (sanity-codegen, status, deprecated for Sanity v3)
- (Sanity TypeGen, recommendedTool, generate TypeScript types from Studio schemas and GROQ)
- (Sanity CLI, minimumVersion, 3.35.0)
- (sanity.cli.ts, mustContain, projectId and dataset (e.g., from env vars))
- (schema definitions, shouldUse, defineType and defineField to enable accurate type generation)
- (type generation workflow, includes, installing TypeGen, pointing to schema entry, running generator, committing types, and adding generation to CI)
- (generated types, usedFor, autocompletion, safer refactors, type-checked GROQ query results)

### {GOAL}
Learn how to auto-generate TypeScript types from your Sanity schema to improve DX, reduce bugs, and boost confidence in your content modeling.

### {PREREQS}
- General familiarity with the article topic

### {STEPS}
1. Follow the detailed walkthrough in the article content below.

<!-- llm:goal="Learn how to auto-generate TypeScript types from your Sanity schema to improve DX, reduce bugs, and boost confidence in your content modeling." -->
<!-- llm:prereq="General familiarity with the article topic" -->
<!-- llm:output="Completed outcome: Learn how to auto-generate TypeScript types from your Sanity schema to improve DX, reduce bugs, and boost confidence in your content modeling." -->

# How to Generate TypeScript Types for Your Sanity V3 Schema
> Learn how to auto-generate TypeScript types from your Sanity schema to improve DX, reduce bugs, and boost confidence in your content modeling.
Matija Žiberna · 2025-05-16

Working with typed content from your Sanity Content Lake significantly improves developer experience by enabling autocompletion, catching potential data handling errors early, and making refactoring safer. This guide will walk you through the process of generating TypeScript definitions from your Sanity Studio schemas and GROQ queries using Sanity TypeGen.

---

I recently published an update to this article.

[Read the updated guide →](https://www.buildwithmatija.com/blog/sanity-typegen-production-workflow-2025)

## Important Note: `sanity-codegen` is Deprecated

If you've previously used or heard of `sanity-codegen`, please be aware that it is deprecated for Sanity v3. The official and recommended tool for generating types is now **Sanity TypeGen**, provided by the Sanity team.

## Prerequisites

Before you begin, ensure you have the following set up:

1.  **Sanity CLI**: Version 3.35.0 or later. You can check your version with `sanity version`.
2.  **Sanity Studio Project**: A functioning Sanity Studio with your schemas defined.
3.  **Schema Definitions**: Your schema types should be correctly defined (e.g., using `defineType`, `defineField`).
4.  **`sanity.cli.ts` Configuration**: Your `sanity.cli.ts` file at the root of your project should be correctly configured with your `projectId` and `dataset`. For example:

    ```typescript
    // sanity.cli.ts
    import {{ defineCliConfig }} from 'sanity/cli'

    const projectId = process.env.NEXT_PUBLIC_SANITY_PROJECT_ID
    const dataset = process.env.NEXT_PUBLIC_SANITY_DATASET

    export default defineCliConfig({{ api: {{ projectId, dataset }} }}) 
    // Add your studioHost or other configurations as needed
    ```
    *(This example is based on your `sanity.cli.ts`)*

5.  **Schema Entry Point**: Your schemas should be consolidated and exported using `createSchema` in a central file, typically `index.ts` within your schema types directory. For example:

    ```typescript
    // src/sanity/schemaTypes/index.ts
    import {{ createSchema }} from 'sanity'

    import {{ blockContentType }} from './blockContentType'
    import {{ categoryType }} from './categoryType'
    import {{ postType }} from './postType'
    import {{ authorType }} from './authorType'
    import emailSubscription from './emailSubscription'
    import contactSubmission from './contactSubmission'

    export const schema = createSchema({{
      name: 'default',
      types: [blockContentType, categoryType, postType, authorType, emailSubscription, contactSubmission],
    }})
    ```
    *(This example is based on your `src/sanity/schemaTypes/index.ts`)*

## Type Generation Workflow

The process involves two main steps executed via the Sanity CLI:

### Step 1: Extract Your Schema

First, you need to extract your Studio's schema into a static JSON representation. Navigate to your project's root directory (where `sanity.cli.ts` is located) and run:

```bash
sanity schema extract
```

This command will:
*   Read your schema definitions.
*   Output a `schema.json` file in your project root (by default).

You should see a confirmation message:
```
✔ Extracted schema
```

This `schema.json` file is an intermediate representation that Sanity TypeGen uses in the next step.

### Step 2: Generate TypeScript Types

Once you have the `schema.json` file, you can generate the TypeScript definitions:

```bash
sanity typegen generate
```

This command will:
*   Read the `schema.json` file.
*   Scan your project (by default, `./src` and its subdirectories) for GROQ queries written using the `groq` template literal or `defineQuery`.
*   Generate a TypeScript file, typically named `sanity.types.ts` (by default), in your project root. This file contains:
    *   Types for all your Sanity schema documents and objects.
    *   Types for the results of your GROQ queries.

You should see a confirmation message similar to:
```
✔ Generated TypeScript types for X schema types and Y GROQ queries in Z files into: ./sanity.types.ts
```

## Understanding the Output

*   **`schema.json`**: This file is a static representation of your schema. You generally don't need to interact with it directly, but it's crucial for the type generation process. You can add this file to your `.gitignore` if you regenerate it as part of your build or development process.
*   **`sanity.types.ts`**: This is the golden file! It contains all the TypeScript definitions. You'll import types from this file into your application code.
    *(Your generated `sanity.types.ts` file will include types like `SanityImageAsset`, `Code`, `PostQueryResult`, etc., based on your specific schemas and queries.)*

## Using Generated Types

With `sanity.types.ts` generated, you can now strongly type your Sanity data in your frontend or other applications:

*   **Catch Bugs**: TypeScript will help you catch errors if you try to access non-existent properties or use incorrect data types.
*   **Autocompletion**: Enjoy autocompletion for document fields and GROQ query results in your code editor.
*   **Easier Refactoring**: When you change your Sanity schemas, regenerating types will immediately highlight parts of your code that need updating.

### Automatic Sanity Client Type Inference

If you use `defineQuery` from the `groq` package to define your GROQ queries, the Sanity Client (`@sanity/client` or `next-sanity`) can automatically infer the return types when you use `client.fetch()`, provided that your `sanity.types.ts` file is included in your `tsconfig.json`.

Example:
```typescript
// your-queries.ts
import { defineQuery } from 'groq';

export const MY_POSTS_QUERY = defineQuery(`*[_type == "post"]{ title, slug }`);

// your-data-fetching-function.ts
import client from './sanityClient'; // Your configured Sanity client
import { MY_POSTS_QUERY } from './your-queries';

async function getPosts() {
  const posts = await client.fetch(MY_POSTS_QUERY);
  // 'posts' will be automatically typed as MY_POSTS_QUERYResult (or similar)
  // posts[0].title -> autocompletes and is type-checked!
  return posts;
}
```

### Example: Typing a Fetched Blog Post in `BlogPostPage`

Let's look at how you can use the generated types in a real component, like your `src/app/blog/[slug]/page.tsx`.

**Before (Using a Custom Interface):**

You might have previously defined a custom interface for your blog post data and used it with your fetching function:

```typescript
// Potentially a custom interface you defined manually
interface CustomPostInterface {
  _id: string;
  title?: string;
  subtitle?: string;
  // ... other fields manually typed
  author: { name?: string; /* ... */ };
  categories?: string[];
  body?: any[]; // For Portable Text
  markdownContent?: string;
  // etc.
}

// In your BlogPostPage component:
// import { sanityFetch } from 'path/to/your/sanityFetch';
// import { POST_QUERY } from 'path/to/your/queries';

// const post = await sanityFetch<CustomPostInterface>({ // Using the custom interface
//   query: POST_QUERY,
//   params: { slug },
//   tags: ['post']
// });

// if (!post) {
//   // Handle post not found
// }

// Accessing data: post.title, post.author.name
```

**After (Using Generated Types):**

With `sanity.types.ts` generated, you can replace `CustomPostInterface` with the type generated specifically for your `POST_QUERY`.

1.  **Import the Generated Type**:
    Your `sanity.types.ts` file will contain a type for your `POST_QUERY`. Based on your provided file, this is named `POST_QUERYResult`.

    ```typescript
    import type { POST_QUERYResult } from 'path/to/your/sanity.types'; // Adjust path if needed
    ```

2.  **Using the Generated Type in `sanityFetch`**:

    ```typescript
    // src/app/blog/[slug]/page.tsx (relevant part)
    import { sanityFetch } from 'path/to/your/sanityFetch'; // Assuming you have a utility like this
    import { POST_QUERY } from 'path/to/your/queries'; // Your GROQ query
    import type { POST_QUERYResult } from '../../../sanity.types'; // Correct relative path to sanity.types.ts

    // ...

    export default async function BlogPostPage({
      params,
    }: {
      params: Promise<{ slug: string }> // or { slug: string }
    }) {
      const { slug } = await params;
      const post = await sanityFetch<POST_QUERYResult>({ // Use the generated type
        query: POST_QUERY,
        params: { slug },
        tags: ['post']
      });

      if (!post) {
        // ... (post not found handling)
        // The type of 'post' here is correctly inferred as 'null' if POST_QUERYResult is a union with null
        return <div>Post not found</div>;
      }

      // Now 'post' is typed according to POST_QUERYResult.
      // For example, post.title, post.author.name, post.body etc. are all type-checked.
      // jsonLd, header elements, PortableText component, etc., all benefit from these types.
      // console.log(post.title); // Autocomplete and type safety!
      
      // ... rest of your component
    }
    ```

**Important Note on `POST_QUERYResult` being `null`:**

In the `sanity.types.ts` file you provided, `POST_QUERYResult` is currently defined as `null`:
```typescript
// Source: ./src/sanity/lib/queries.ts
// Variable: POST_QUERY
// Query: *[_type == "post" && slug.current == $slug][0] { ... }
export type POST_QUERYResult = null;
```
This usually means one of two things:
*   The query can legitimately return `null` (which `*[_type == "post" && slug.current == $slug][0]` does if no post is found), and TypeGen has determined this is the *only* possible type.
*   More likely, Sanity TypeGen was unable to fully determine the structure of the post when a post *is* found, and defaulted to `null`. This can happen if there are complexities in the schema or query that TypeGen doesn't fully support, or if the setup isn't complete (e.g., `sanity.types.ts` not in `tsconfig.json`'s include path during a previous generation).

Ideally, for a query like `POST_QUERY` that fetches a single document, the generated `POST_QUERYResult` should be a union type representing the structure of the document when found, and `null` when not. For example:

```typescript
// What POST_QUERYResult should ideally look like (simplified)
export type POST_QUERYResult = {
  _id: string;
  _type: "post";
  title?: string;
  subtitle?: string;
  // ... all other fields from your query projection ...
  author?: {
    name?: string;
    // ... other author fields ...
  };
  categories?: Array<string>;
} | null;
```

If your query types are consistently `null` or `Array<never>`, please:
1.  Ensure `sanity schema extract` runs successfully before `sanity typegen generate`.
2.  Verify that your GROQ queries are correctly defined (e.g., assigned to variables, using `groq` tag from the `groq` package or `defineQuery`).
3.  Check that `sanity.types.ts` is included in your `tsconfig.json` file's `include` array.
4.  Consult the "Troubleshooting & Tips" section below and the official Sanity TypeGen documentation.

By using the (correctly generated) `POST_QUERYResult`, you leverage the full power of TypeScript, getting autocompletion and type safety based directly on your Sanity schema and GROQ queries, eliminating the need to maintain manual interfaces.

## Configuration (Optional)

While the default settings work well for many projects, you can customize the behavior of `sanity typegen generate` by creating a `sanity-typegen.json` file in your project root.

Example `sanity-typegen.json` with default values:
```json
{
  "path": "./src/**/*.{ts,tsx,js,jsx}",
  "schema": "schema.json",
  "generates": "./sanity.types.ts",
  "overloadClientMethods": true
}
```
*   `path`: Glob pattern(s) for where TypeGen should look for GROQ queries.
*   `schema`: Path to your `schema.json` file.
*   `generates`: Path for the output TypeScript definitions file.
*   `overloadClientMethods`: Set to `false` to disable automatic Sanity Client method overloading.

## Adding a Script to `package.json`

To streamline the process, you can add a script to your `package.json`:

```json
// package.json
"scripts": {{
  // ... other scripts
  "generate:types": "sanity schema extract && sanity typegen generate"
}},
```
Then you can run `pnpm generate:types` (or `npm run` / `yarn`) to perform both steps.

*(This matches the script we added to your `package.json`.)*

Furthermore, to ensure your types are always current before building your application, you can integrate this script into your existing `build` script:

```json
// package.json
"scripts": {{
  "dev": "pnpm next dev --turbopack",
  "generate:types": "sanity schema extract && sanity typegen generate",
  "build": "pnpm generate:types && pnpm next build", // Updated build script
  "start": "pnpm next start",
  "lint": "pnpm next lint"
}},
```
This way, every time you run `pnpm build`, it will first regenerate your Sanity types and then proceed with the Next.js build process.

## Troubleshooting & Tips

*   **`tsconfig.json`**: Ensure your generated `sanity.types.ts` file is included in the `include` array of your project's `tsconfig.json`. For example:
    ```json
    // tsconfig.json
    {
      "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", "sanity.types.ts"],
      // ... other configurations
    }
    ```
*   **Unique Query Names**: TypeGen requires all GROQ queries it processes to have unique variable names.
*   **GROQ Syntax**: Ensure your GROQ queries are valid.
*   **Unsupported Features**: While TypeGen supports most schema types and GROQ features, some complex or niche cases might result in `unknown` types. Check the official Sanity documentation for the latest on supported features.
*   **Schema Errors**: If TypeGen produces empty types (e.g., `null` or `Array<never>`), incomplete types, or no types for your custom documents, it often indicates an underlying error in your Sanity schema definitions. Run `npx sanity dev` (or `sanity dev`) and check the terminal and browser console for errors. The Sanity Studio must load correctly and show all your custom types for `sanity schema extract` and `sanity typegen generate` to work as expected. Resolving schema errors reported by the Studio is a critical first step.
*   **Query Projections vs. Schema Definitions**: If your generated types look off or are missing fields you expect (even if not `null` or `Array<never>`), double-check that the fields selected in your GROQ queries (e.g., within `defineQuery`) actually match the fields defined in your corresponding Sanity schemas (like `authorType.ts`, `postType.ts`, etc.). TypeGen generates types based on what your query *requests*, so if a field isn't in the query's projection, it won't be in the resulting type, regardless of whether it's in the schema. You can freely adjust your query projections to include or exclude fields as needed.

By following this guide, you can effectively leverage Sanity TypeGen to bring strong typing to your Sanity projects, leading to more robust and maintainable code.

## LLM Response Snippet
```json
{
  "goal": "Learn how to auto-generate TypeScript types from your Sanity schema to improve DX, reduce bugs, and boost confidence in your content modeling.",
  "responses": [
    {
      "question": "What does the article \"How to Generate TypeScript Types for Your Sanity V3 Schema\" cover?",
      "answer": "Learn how to auto-generate TypeScript types from your Sanity schema to improve DX, reduce bugs, and boost confidence in your content modeling."
    }
  ]
}
```