How to get schema types into a TypeScript project with a React.js app and Sanity.io

11 replies
Last updated: Feb 3, 2023
Hi - so you guys have probably dealt with this a lot already - but my head is exploding by finding the best solution on following problem:
I got a react.js app with typescript and a brand new sanity project. How do i get my schema types into the TS Project
I tried creating types with the
JSON to TS vsc PluginI tried the sanity-codegen npm pkg in my sanity project but that only throws a bunch of errors.

Anyone got any ideas or solutions already?
Appreciate it
🙏
AI Update

Totally feel your pain! The good news is that Sanity has an official built-in solution called TypeGen that should solve this for you. The sanity-codegen package you tried is an older third-party tool that's not maintained for Sanity v3, which explains the errors.

Here's the modern approach using Sanity's official TypeGen:

Quick Setup (2 steps)

1. Extract your schema (run this in your Sanity project folder):

npx sanity@latest schema extract

This creates a schema.json file with your schema structure.

2. Generate TypeScript types:

npx sanity@latest typegen generate

This creates a sanity.types.ts file with all your schema types! 🎉

Using the Types in Your React App

After generation, you can import and use the types:

import type { Post, Author } from './sanity.types'

// Type-safe document creation
await client.create<Post>({
  _type: "post",
  title: "My Post",
  // TypeScript will autocomplete and validate fields!
})

Multi-Folder Setup (Sanity + React in separate folders)

If your Sanity studio and React app are in different folders, create a sanity-typegen.json in your Sanity project:

{
  "path": "../my-react-app/src/**/*.{ts,tsx}",
  "schema": "./schema.json",
  "generates": "../my-react-app/src/sanity/sanity.types.ts"
}

This tells TypeGen where to find your React files and where to output the types.

Bonus: Type-Safe GROQ Queries

For even better DX, wrap your GROQ queries with defineQuery to get typed query results:

import { defineQuery } from 'groq'

const postsQuery = defineQuery(`*[_type == "post"]{ title, author-> }`)

// TypeGen will generate types for this query result!
const posts = await client.fetch(postsQuery)

Requirements

You'll need Sanity CLI version 3.35.0 or later. Update if needed:

npm install -g sanity@latest

Pro tip: Run sanity typegen generate whenever you change your schema to keep types in sync. You can also use sanity typegen generate --watch during development to automatically regenerate types when your schema changes!

You do lose out on the power of GROQ queries, but automatic type/hook generation is too good to pass up.
Thanks - i've been tryna ditch GraphQL as long as possible, but I guess my time has also come now 🙂
user T
do you maybe have a public GitHub Repo as example?
Not a public one, no. Sorry.
codegen.ts
:

import type { CodegenConfig } from "@graphql-codegen/cli";

const config: CodegenConfig = {
  overwrite: true,
  schema: "<https://YOUR_PROJECT_ID_HERE.api.sanity.io/v1/graphql/production/default>",
  ignoreNoDocuments: true,
  documents: ["./src/graphql/**/*.{graphql,ts}"],
  generates: {
    "./src/services/index.ts": {
      plugins: [
        "typescript",
        "typescript-operations",
        "typescript-react-query",
      ],
      config: {
        fetcher: "../lib/fetcher#fetchData",
        exposeDocument: true,
        exposeQueryKeys: true,
        exposeMutationKeys: true,
        exposeFetcher: true,
        strictScalars: true,
        scalars: {
          Date: "string",
          DateTime: "string",
          JSON: "{ [key: string]: any }",
        },
      },
    },
  },
};

export default config;
Example graphql query:

import { gql } from "@apollo/client";

export const GET_ALL_ARTICLES = gql`
  query getAllArticles {
    allArticle(where: { _: { is_draft: false } }) {
      _id
      slug {
        current
      }
    }
  }
`;
lib/fetcher/index.ts
:
export const fetchData = &lt;TData, TVariables&gt;(
_query_: string,

variables?: TVariables,
options?: RequestInit[“headers”]): (() =&gt; Promise&lt;TData&gt;) =&gt; {
return async () =&gt; {
const RESPONSE = await fetch(process.env.NEXT_PUBLIC_SANITY_GRAPHQL_URL, {
method: “POST”,
headers: {
Authorization:
Bearer ${process.env.NEXT_PUBLIC_SANITY_TOKEN}
, “Content-Type”: “application/json”,
...
options, },
body: JSON.stringify({
query,
variables,
}),
});

const JSON_RESPONSE = await RESPONSE.json();

if (JSON_RESPONSE.errors) {
const { message } = JSON_RESPONSE.errors[0] || {};
throw new Error(message || “Error...“);
}

return JSON_RESPONSE.data;
};
};
user T
thank you so much 🙏 Will try it out! Since i got 0xp with GraphQL I'm gonna start with some tutorials on that topic first
user T
, seems like you’re exposing the sanity token client side, should you do that?

Sanity – Build the way you think, not the way your CMS thinks

Sanity is the developer-first content operating system that gives you complete control. Schema-as-code, GROQ queries, and real-time APIs mean no more workarounds or waiting for deployments. Free to start, scale as you grow.

Was this answer helpful?