Generated banner for Sanity Best Practices
Generated logo for Sanity Best Practices

Sanity Best Practices

SanityOfficialPublished by Sanity

  • Skill
  • Tool integration
  • Software development
  • MIT
  • No issues found

From the author

Sanity development best practices for schema design, GROQ queries, TypeGen, Visual Editing, images, Portable Text, Studio structure, localization, migrations, Sanity Functions, webhooks, Blueprints, and framework integrations such as Next.js, Nuxt, Astro, Remix, SvelteKit, Angular, Hydrogen, the App SDK, and Studios or apps running in the Sanity Dashboard (defineApplication). Use this skill whenever working with Sanity schemas, defineType or defineField, GROQ or defineQuery, content modeling, Presentation or preview setups, Sanity-powered frontend integrations, event-driven content automation, documentEventHandler, defineDocumentFunction, defineMediaLibraryAssetFunction, @sanity/functions, @sanity/blueprints, sanity.blueprint.ts, event-driven content automation, or when reviewing and fixing a Sanity codebase.
— the description by Sanity

AutoGineer summary

Written by AutoGineer from the skill’s own files, not by the author

This skill provides a comprehensive collection of best practices, architectural patterns, and integration guides for the Sanity ecosystem. It covers the full development lifecycle, including schema design, GROQ query optimization, TypeGen configuration, and framework-specific integrations like Next.js, Angular, and Remix. It also includes guidance on infrastructure management via Blueprints, content automation with Sanity Functions, and building custom applications with the Sanity App SDK.

What it does

  • Provides standardized patterns for Sanity schema design and content modeling.
  • Offers integration guides for major frontend frameworks and the Sanity App SDK.
  • Defines best practices for GROQ query performance and type safety with TypeGen.
  • Outlines rules for media handling, including video asset management.
  • Details configuration for Sanity Studio structure and Visual Editing.
  • Explains infrastructure-as-code workflows using Sanity Blueprints.

When to use it

  • When starting a new Sanity project or onboarding team members.
  • When integrating Sanity with frameworks like Next.js, Astro, or Angular.
  • When optimizing GROQ queries or implementing type-safe data fetching.
  • When designing complex content schemas or implementing localization.
  • When setting up live previews, Visual Editing, or custom Studio structures.
  • When automating content workflows or managing infrastructure via Blueprints.

Advantages

  • Maintained by Sanity, ensuring alignment with current platform standards.
  • Reduces technical debt by enforcing consistent schema and query patterns.
  • Provides clear decision matrices for complex topics like video and localization.
  • Includes framework-specific guidance to accelerate frontend integration.
  • Promotes maintainable project structures and type-safe development workflows.

Usage examples

“What are the best practices for handling video in Sanity?”
The skill advises against using standard file assets for production video due to bandwidth costs, recommending the Sanity Media Library with Mux for Enterprise plans or dedicated services like Mux, YouTube, or Vimeo for others.
“How do I set up TypeGen for an Angular monorepo?”
The skill provides a configuration example for studio/sanity.cli.ts to point TypeGen at the Angular source files, ensuring generated types are correctly integrated into the Angular project.
“What is the recommended way to structure singleton documents?”
The skill recommends using explicit document IDs for singleton documents and controlling them via the Studio Structure, while letting Sanity generate IDs for all other document types.
“How should I fetch data in a React app using the App SDK?”
The skill mandates wrapping data-fetching components in Suspense, using documentId as a React key, and preferring useDocuments and useDocumentProjection over raw GROQ hooks.

Install

  1. Download the skill as a ZIP:

    https://autogineer.com/agent-skills/sanity-io/sanity-best-practices/download
  2. Unzip it into the cross-client skills folder of your project:

    mkdir -p .agents/skills && unzip sanity-best-practices.zip -d .agents/skills

Documentation by Sanity

Sanity Best Practices

Comprehensive best practices and integration guides for Sanity development, maintained by Sanity. Use the quick reference below to load only the one or two topic files that match the task.

When to Apply

Reference these guidelines when:

  • Setting up a new Sanity project or onboarding

  • Integrating Sanity with a frontend framework (Next.js, Nuxt, Astro, Remix, SvelteKit, Hydrogen)

  • Writing GROQ queries or optimizing performance

  • Designing content schemas

  • Implementing Visual Editing and live preview

  • Working with images, Portable Text, or page builders

  • Configuring Sanity Studio structure

  • Setting up TypeGen for type safety

  • Implementing localization

  • Migrating content from other systems

  • Building custom apps with the Sanity App SDK

  • Developing or deploying a Studio or app in the Sanity Dashboard

  • Managing infrastructure with Blueprints

  • Automating content workflows with Sanity Functions or webhooks

Global Rules

  • Let Sanity generate _id values for ordinary documents. Do not create deterministic UUIDs, slug-derived IDs, or legacy-system IDs when creating documents.

  • Model relationships with reference fields, then resolve related documents with GROQ lookups, source-key fields, or returned _id values from created documents.

  • Use explicit document IDs mainly for singleton documents controlled by Studio Structure, including localized singletons such as homePage-en.

Video

  • Do not store or serve video from Sanity file assets for production playback. File assets are delivered as raw downloads with no transcoding or adaptive streaming, and video traffic drives very high bandwidth usage and unexpectedly large bills.

  • On Enterprise plans with the video add-on, use Sanity Media Library for video: uploads are transcoded and streamed adaptively via Mux. Model video fields with defineVideoField() from sanity/media-library and play them with @mux/mux-player-react using the asset's playback ID.

  • On other plans, use a dedicated video service: install sanity-plugin-mux-input to upload and manage videos in your Mux account from the Studio, or host video on a platform such as YouTube or Vimeo and store only the embed URL in Sanity.

  • Small clips and short previews in a file field are acceptable, but any user-facing video at scale must go through Media Library or a streaming service.

Quick Reference

Integration Guides
  • get-started - Interactive onboarding for new Sanity projects

  • nextjs - Next.js App Router, Live Content API, standalone Studio

  • nuxt - Nuxt integration with @nuxtjs/sanity

  • angular - Angular integration with @sanity/client, signals, resource API

  • astro - Astro integration with @sanity/astro

  • remix - React Router / Remix integration

  • svelte - SvelteKit integration with @sanity/svelte-loader

  • hydrogen - Shopify Hydrogen with Sanity

  • project-structure - Standalone Studio and monorepo patterns

  • app-sdk - Custom applications with Sanity App SDK

  • dashboard - Studios and apps in the Sanity Dashboard: defineApplication, local dev, deploys, Dashboard hooks

  • blueprints - Infrastructure as Code: blueprint files, stacks, plan/deploy workflow, error recovery, CI deploys

  • functions - Automating content workflows with Sanity Functions and webhooks

Topic Guides
  • groq - GROQ query patterns, type safety, performance optimization

  • schema - Schema design, field definitions, validation, deprecation patterns

  • visual-editing - Presentation Tool, Stega, overlays, live preview

  • page-builder - Page Builder arrays, block components, live editing

  • portable-text - Rich text rendering and custom components

  • image - Image schema, URL builder, hotspots, LQIP, Next.js Image

  • studio-structure - Desk structure, singletons, navigation

  • typegen - TypeGen configuration, workflow, type utilities

  • seo - Metadata, sitemaps, Open Graph, JSON-LD

  • localization - i18n patterns, document vs field-level, locale management

  • migration - Content import overview (see also migration-html-import)

  • migration-html-import - HTML to Portable Text with @portabletext/block-tools

How to Use

Start with the single framework or topic guide that best matches the request, then read additional references only when the task crosses concerns. Use these reference files for detailed explanations and code examples:

references/groq.md
references/schema.md
references/nextjs.md

Each reference file contains:

  • Comprehensive topic or integration coverage

  • Incorrect and correct code examples

  • Decision matrices and workflow guidance

  • Framework-specific patterns where applicable

Files

references/blueprints.md


title: Sanity Blueprints description: Rules for Sanity Blueprints, the Infrastructure as Code solution for managing Sanity resources declaratively. Covers blueprint files, stacks and scope, the plan/deploy workflow, deletion policies, error recovery, and CI deployment.

Sanity Blueprints

Sanity's Infrastructure as Code (IaC) solution. Define resources declaratively in sanity.blueprint.ts, track the file in version control, preview changes with plan, apply them with deploy. Blueprints is the only way to deploy Sanity Functions.

The CLI is built for unattended use: every command accepts --json, all IDs can be supplied via flags or environment variables, and --help on any command is the authoritative reference. Always run via npx sanity@latest blueprints <command>.

Mental Model

Blueprint file (code, intent) → Stack (deployed state) → Resources (real infrastructure)
ConceptWhat it is
Blueprint filesanity.blueprint.ts, the declarative manifest of desired resources. The source of truth: if it's not in the file, it's not deployed
ResourceOne managed thing (function, CORS origin, webhook, role, robot token, dataset). Identified by a unique name
DefinerTyped helper from @sanity/blueprints (e.g. defineCorsOrigin) that declares a resource and validates input as you write
StackThe deployed counterpart of the file: the live resources on Sanity's side. One file can deploy to many Stacks (e.g. staging, production)
Config file.sanity/blueprint.config.json, links the local file to a Stack and records scope. Gitignored by init automatically; not secret
Scopeproject or organization. Determines which resource types a Stack can manage
Key behaviors
  • Resources are matched by name between file and Stack. plan buckets every resource as create, update, no-op, or destroy.

  • There is no rename. Changing a resource's name destroys the old resource and creates a new one under the new name.

  • Removal is destruction. Deleting a resource from the file destroys it on the next deploy (subject to its deletion policy). blueprints destroy is equivalent to deploying an empty file.

  • Deploys apply in dependency order. On partial failure, completed changes are rolled back best-effort in reverse order. Rollback is not atomic; some actions may be irreversible.

Three different "deploy" commands

Do not confuse these:

CommandWhat it deploys
sanity blueprints deployInfrastructure resources defined in sanity.blueprint.ts
sanity schema deployStudio schema to Content Lake (for MCP and editor access)
sanity deployThe Studio application to Sanity hosting

The Blueprint File

sanity.blueprint.ts default-exports a defineBlueprint call. TypeScript is idiomatic (definers validate inline and provide editor help); .js and hand-written .json also work but new files should use TypeScript.

import {defineBlueprint, defineCorsOrigin, defineDocumentFunction} from '@sanity/blueprints'

export default defineBlueprint({
  values: {
    corsOrigin: 'https://studio.example.com',
  },
  resources: [
    defineCorsOrigin({
      name: 'studio-cors',
      origin: '$.values.corsOrigin',
    }),
    defineDocumentFunction({
      name: 'first-published',
      event: {
        on: ['create', 'update'],
        filter: "_type == 'post' && !defined(firstPublished)",
      },
    }),
  ],
})
  • resources is an array of definer outputs. Every resource requires a unique name.

  • values holds reusable file-level constants (always strings), referenced as $.values.<key>.

  • Resource-type-specific fields are documented in the typed reference: https://reference.sanity.io/_sanity/blueprints/

Discovering available resources

The definers exported by @sanity/blueprints are the canonical list of what Blueprints can manage. Discover them by inspecting the package's exports (every define* function is a resource definer) and reading their TSDoc for fields, defaults, and examples, or browse the typed reference at https://reference.sanity.io/_sanity/blueprints/. Do not rely on a memorized list; new definers ship additively.

When the Stack is organization-scoped, project-contained resources (CORS origins, webhooks, datasets, functions) must set their project field explicitly.

For the semantics of an individual resource type (what a CORS origin, webhook, or role does and which values make sense), consult that feature's own Sanity documentation. This reference covers how Blueprints manages resources, not the resources themselves.

References and the Resource Graph

Resources reference values and each other with $ string paths:

SyntaxResolvesWhen
$.values.<key>A constant from the values blockAt file evaluation
$.resources.<name>Another resource in the fileAt deploy time; creates a dependency edge
$.resources.<name>.idThe generated ID of that resource, as a stringAt deploy time

Blueprints builds the dependency graph from these references and orders work so dependencies are created first. Unresolved references and cycles fail validation before anything is deployed. To force ordering without a data reference, use lifecycle: {dependsOn: '$.resources.<name>'}.

Referencing an existing project

The recommended pattern is an environment variable surfaced through values:

import 'dotenv/config' // the CLI does not read .env files itself

export default defineBlueprint({
  values: {
    projectId: process.env.SANITY_PROJECT_ID ?? '',
  },
  resources: [
    defineCorsOrigin({
      name: 'app-cors',
      project: '$.values.projectId',
      origin: 'https://www.example.com',
    }),
  ],
})

Deletion Policies

Set lifecycle.deletionPolicy on a resource to control what removal means:

PolicyRemoved from fileStack destroyedNotes
allowResource destroyedResource destroyedDefault for stateless resources
retainDeployment failsResource detached, kept aliveDefault for stateful resources like datasets
replaceResource destroyedResource destroyedUpdates become destroy + recreate
protectDeployment failsDeployment failsResource is never updated or destroyed

Use protect for production-critical stateful resources:

defineDataset({
  name: 'production',
  lifecycle: {deletionPolicy: 'protect'},
})

A deletion-policy violation is a pre-deploy validation failure, not a partial deploy: fix the file (or consciously change the policy) and re-run plan.

Stacks, Environments, and Scope

One blueprint file deploys to many Stacks. The --stack <name-or-id> flag (on deploy, plan, info, logs) selects the target; without it, the Stack recorded in .sanity/blueprint.config.json is used.

  • Only init creates Stacks. --stack never creates a Stack on a miss, so CI cannot accidentally provision infrastructure. Re-run blueprints init . to add another Stack for the same file.

  • Accounts are currently limited to three Stacks per project scope.

  • For per-environment differences, read an environment variable inside the file (e.g. process.env.SANITY_ENV) and deploy with the env var and --stack together. SANITY_ENV is a convention your file reads, not a CLI feature.

Project vs organization scope
Project scopeOrganization scope
Stored in config asprojectIdorganizationId
Resources default toThe projectNothing; each project-contained resource must name its project
Org-scoped resource typesNot availableAvailable

Organization scope is recommended for anything beyond a single-project experiment. Convert an existing Stack with blueprints promote (safe, idempotent, additive, but one-way; requires organization-update admin permission).

CLI Commands

npx sanity@latest blueprints <command>
CommandPurpose
init [dir]Create a blueprint file and provision a remote Stack. The only command that creates Stacks
planPreview the diff against the Stack. Read-only, always safe
deployApply the file to the Stack (-m message, --no-wait to queue and return)
infoShow Stack status and deployed resources. Remote read; works without local files
stacksList Stacks (--include-projects with --organization-id for org-wide audit)
configView or edit .sanity/blueprint.config.json (--edit with ID flags for scripting)
logsDeployment logs (--watch, --limit 1-500, --since/--before ISO timestamps)
promoteConvert a Stack from project to organization scope. One-way (--force to skip confirmation)
destroyDestroy the Stack and its resources (--force to skip confirmation). Local files remain
doctorDiagnose local/remote configuration issues (--fix to resolve interactively)
mint-deploy-tokenMint a long-lived robot token for CI deploys (--print for shell pipelines)

Running npx sanity@latest blueprints --help will show the latest available commands.
Use npx sanity@latest blueprints <command> --help to see options and examples for a specific command.

Function scaffolding, local testing, secrets, and function logs live under npx sanity@latest functions <command> (add, dev, test, env add|list|remove, logs). See the functions reference.

Standard workflow
npx sanity@latest blueprints init .   # once: create file + Stack
# edit sanity.blueprint.ts
npx sanity@latest blueprints plan     # preview the diff, catch validation errors safely
npx sanity@latest blueprints deploy   # apply
npx sanity@latest blueprints info     # verify deployed state

Always run plan before deploy. It is read-only and surfaces validation errors, reference problems, scope mismatches, and deletion-policy violations without touching anything.

Exit codes

deploy and destroy return: 0 succeeded, 2 failed, 75 accepted but completion could not be confirmed.

✅ Correct — treat exit 75 as unknown, then verify:

npx sanity@latest blueprints deploy || status=$?
if [ "${status:-0}" -eq 75 ]; then
  npx sanity@latest blueprints info --json   # confirm actual Stack state
fi

❌ Incorrect — treating any nonzero exit as failure and retrying. Exit 75 means the deployment was accepted; blindly redeploying can queue duplicate operations (deploy also refuses to start while another operation is in progress).

Scope resolution and environment variables

For organizationId, projectId, and stackId, the first source that resolves wins: CLI flags → environment variables → the blueprint file module → the local config file.

VariablePurpose
SANITY_AUTH_TOKENAuth token (in CI, the minted deploy token)
SANITY_ORGANIZATION_ID / SANITY_PROJECT_IDScope
SANITY_BLUEPRINT_STACK_IDTarget Stack
SANITY_BLUEPRINT_PATHPath to the blueprint file or its directory
SANITY_ASSET_TIMEOUTSeconds to wait for resource asset processing (default 60)

The CLI does not load .env files. Export variables in the shell or import dotenv/config inside the blueprint file.

Errors and Recovery

Errors include a name, a human-readable message, and the resource path, which maps to a specific resource in your file. Five failure kinds:

FailureCaughtWhat to do
Validation error (missing/invalid field, malformed file)Before deployFix the file at the reported resource path
Unresolved reference or dependency cycleBefore deployFix the $.resources.<name> reference or break the cycle
Scope mismatch (org-scoped resource on project Stack, or no resolvable project)Before deploySet the resource's project field, or promote the Stack
Deletion-policy violation (change would remove a retain/protect resource)Before deployRestore the resource, or deliberately relax its policy
Execution failure (underlying service rejected the change)During deployRead blueprints logs, fix the cause, redeploy

Recovery procedure after any failure:

  1. Read the error message and resource path (blueprints logs re-reads failure output).

  2. Fix the blueprint file (or the external condition).

  3. Run blueprints plan to confirm the corrected diff.

  4. Run blueprints deploy again.

Retrying without changing anything only makes sense for transient execution failures; the first four failure kinds are deterministic and will fail identically until the file changes. If local and remote state seem inconsistent, run blueprints doctor (and doctor --fix) before attempting anything destructive. After a partial-deploy rollback, blueprints info shows what is actually live; resources owned by other Stacks are never touched by rollback.

CI/CD

Recommended workflow: plan on pull requests, deploy on merge to main. Official GitHub Actions: https://github.com/sanity-io/blueprints-actions

# one-time, by a human with access:
npx sanity@latest blueprints mint-deploy-token --label "ci-deploy" --print

The minted robot token has exactly the role needed to plan, deploy, and destroy (blueprints-deployer for project scope, blueprints-deployer-robot for organization scope) and is revocable under Robots in Sanity Manage. Store it as a CI secret, then in the pipeline:

export SANITY_AUTH_TOKEN=<the minted token>
export SANITY_ORGANIZATION_ID=<orgId>        # or SANITY_PROJECT_ID
export SANITY_BLUEPRINT_STACK_ID=<stackId>
npx sanity@latest blueprints deploy --json --message "CI deploy"

✅ Correct — secrets via server-side function env vars:

npx sanity@latest functions env add my-function API_KEY sk-...

❌ Incorrect — secrets in the blueprint file's env block, which is committed to git:

defineDocumentFunction({
  name: 'my-function',
  env: {API_KEY: 'sk-...'},  // committed to version control!
})

Requirements and compatibility

Works with: Claude Code, Claude apps, OpenAI Codex, GitHub Copilot, Cursor, Gemini CLI, Kiro, OpenCode, Goose, Amp, Roo Code, JetBrains Junie, Factory Droid, Devin (ex-Windsurf), Mistral Vibe, OpenHands, Letta

“Works with” means the tool reads the open Agent Skills format; a skill written for one tool may use features others lack.

Security

No issues found by our static scan· No issues found

A static scan is not a guarantee. Skills are third-party content: review a skill before you let an agent use it.

Attribution and licence

Published by
Sanity
Publisher
Sanity (Official)
Source
sanity-io/agent-toolkit · skills/sanity-best-practices · commit 3ed3a14
Licence
MIT License (redistribution allowed)
  • LICENSE (added by AutoGineer from LICENSE)
  • ATTRIBUTION.md (written by AutoGineer)
Synced

AutoGineer is not affiliated with or endorsed by Sanity or Sanity.

Download ZIP

The ZIP holds the skill’s files, its licence files and an ATTRIBUTION.md. Download sanity-best-practices.zip