All skills
sanity-io avatar

/sanity-best-practices

@fc8116b official
by Sanitysanity-io/agent-toolkit187 stars
30

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, and the App SDK. 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.

Use this Skill: https://skilld.dev/gh/sanity-io/agent-toolkit/sanity-best-practices

This session only. Nothing lands on disk.

referenceshydrogen.md

≈1.7k tokens on demand. Your agent reads this file only when SKILL.md points to it.

Sanity + Shopify + Hydrogen Rules

Package: hydrogen-sanity — requires @shopify/hydrogen >= 2025.5.0

1. Architecture Overview

Component Purpose
Shopify Product catalog, inventory, checkout (source of truth for commerce)
Sanity Connect Syncs Shopify data to Sanity in real-time
Sanity Studio Editorial content, rich descriptions, media (enhances Shopify data)
Hydrogen React Router 7 front-end optimized for Shopify

Project Structure:

./
├── /studio    # Sanity Studio
└── /web       # Hydrogen front-end

2. Environment Variables

# web/.env
PUBLIC_STOREFRONT_API_TOKEN="your-public-storefront-token"
PRIVATE_STOREFRONT_API_TOKEN="your-private-storefront-token"
PUBLIC_STORE_DOMAIN="your-store.myshopify.com"
SESSION_SECRET="your-random-session-secret"

# Sanity
SANITY_PROJECT_ID="your-project-id"
SANITY_DATASET="production"
SANITY_API_VERSION="2026-02-01"
SANITY_PREVIEW_TOKEN="your-sanity-viewer-token"  # Viewer token for previews

3. Sanity Client Setup

Vite Config

// web/vite.config.ts
import {hydrogen} from '@shopify/hydrogen/vite'
import {sanity} from 'hydrogen-sanity/vite'

export default defineConfig({
  plugins: [hydrogen(), sanity()],
})

Context Setup

// web/app/lib/context.ts
import {createSanityContext, type SanityContext} from 'hydrogen-sanity'
import {PreviewSession} from 'hydrogen-sanity/preview/session'
import {isPreviewEnabled} from 'hydrogen-sanity/preview'

const previewSession = await PreviewSession.init(request, [env.SESSION_SECRET])

const sanity = await createSanityContext({
  request,
  cache,
  waitUntil,
  client: {
    projectId: env.SANITY_PROJECT_ID,
    dataset: env.SANITY_DATASET,
    apiVersion: env.SANITY_API_VERSION || '2026-02-01',
    useCdn: true,
    stega: {
      enabled: isPreviewEnabled(env.SANITY_PROJECT_ID, previewSession),
      studioUrl: 'http://localhost:3333',
    }
  },
  preview: {
    token: env.SANITY_PREVIEW_TOKEN,
    session: previewSession,
  }
})

Provider Setup (entry.server.tsx)

const {SanityProvider} = context.sanity

const body = await renderToReadableStream(
  <NonceProvider>
    <SanityProvider>
      <ServerRouter context={reactRouterContext} url={request.url} nonce={nonce} />
    </SanityProvider>
  </NonceProvider>,
)

Root Layout (root.tsx)

import {Sanity} from 'hydrogen-sanity'

export function Layout({children}) {
  const nonce = useNonce()
  return (
    <html>
      <body>
        {children}
        <Sanity nonce={nonce} />  {/* Required for client-side */}
        <Scripts nonce={nonce} />
      </body>
    </html>
  )
}

4. Data Fetching

Fetch from both Shopify (GraphQL) and Sanity (GROQ). Use defineQuery for TypeGen support.

Recommended: query + Query component

import {defineQuery} from 'groq'
import {Query} from 'hydrogen-sanity'

const PRODUCT_QUERY = defineQuery(`*[_type == "product" && store.slug.current == $handle][0]{ body }`)

// Loader
export async function loader({params, context: {sanity}}: LoaderFunctionArgs) {
  const initial = await sanity.query(PRODUCT_QUERY, params)
  return {initial}
}

// Component - auto-enables live preview when active
export default function ProductPage({loaderData}) {
  return (
    <Query query={PRODUCT_QUERY} options={{initial: loaderData.initial}}>
      {(data) => <div>{data?.body}</div>}
    </Query>
  )
}

Alternative methods

Method Use Case
sanity.query() + Query Recommended - auto preview mode
sanity.loadQuery() Manual loader integration
sanity.fetch() No preview needed, lightweight
sanity.client Mutations in actions

Images

import {useImageUrl} from 'hydrogen-sanity'

function Hero({image}) {
  const imageUrl = useImageUrl(image)
  return <img src={imageUrl.width(1200).height(600).url()} />
}

Key Insight: Shopify fields synced via Sanity Connect are readOnly. Use Sanity for editorial enhancements only.

5. Visual Editing Setup

Root Layout

// web/app/root.tsx
import {usePreviewMode} from 'hydrogen-sanity/preview'
import {VisualEditing} from 'hydrogen-sanity/visual-editing'

export function Layout({children}: {children?: React.ReactNode}) {
  const previewMode = usePreviewMode()

  return (
    <html>
      <body>
        {children}
        {previewMode ? <VisualEditing action="/api/preview" /> : null}
      </body>
    </html>
  )
}

Preview Route

// web/app/routes/api.preview.ts
export {action, loader} from 'hydrogen-sanity/preview/route'

Content Security Policy

// web/entry.server.tsx
const {nonce, header, NonceProvider} = createContentSecurityPolicy({
  frameAncestors: isPreviewEnabled ? [studioHostname] : [],
  connectSrc: [
    `https://${projectId}.api.sanity.io`,
    `wss://${projectId}.api.sanity.io`,
  ],
})

6. Studio: Presentation Tool

// studio/sanity.config.ts
import {presentationTool} from 'sanity/presentation'

export default defineConfig({
  plugins: [
    presentationTool({
      resolve: {
        locations: {
          product: defineLocations({
            select: { title: 'store.title', slug: 'store.slug.current' },
            resolve: (doc) => ({
              locations: [
                { title: doc?.title || 'Untitled', href: `/products/${doc?.slug}` },
                { title: 'Products', href: `/collections/all` },
              ],
            }),
          }),
        },
      },
      previewUrl: {
        origin: 'http://localhost:3000',
        previewMode: { enable: '/api/preview' },
      },
    }),
  ],
})

7. Commands

# Install dependencies
pnpm add hydrogen-sanity @sanity/client @portabletext/react

# Development (run in separate terminals)
cd studio && pnpm dev    # Studio at localhost:3333
cd web && pnpm dev       # Hydrogen at localhost:3000

# Sanity Manage (CORS, tokens): https://www.sanity.io/manage
pnpm dlx sanity manage

8. Boundaries

  • Always:

    • Query Shopify for commerce data (price, inventory, variants)
    • Query Sanity for editorial content (rich text, custom fields)
    • Use hydrogen-sanity package for Visual Editing
    • Add Hydrogen URL to CORS origins in Sanity Manage
  • Ask First:

    • Before modifying Sanity Connect sync settings
    • Before changing CSP configuration
  • Never:

    • Edit Shopify-synced fields in Sanity (they're readOnly)
    • Expose SANITY_API_TOKEN to client-side code
    • Query Sanity for commerce data that should come from Shopify

Source: SKILL.md on GitHub

2 warnings14d5 checks · Risk SAFE
  • Gen Agent Trust Hub14d

    This skill provides comprehensive Sanity.io development best practices, covering schema design, GROQ queries, and integration with major frontend frameworks. It promotes secure development habits, such as proper management of API tokens and environment variables.

  • Socket14d

    1 alert: gptAnomaly

  • Snyk14d

    Risk: LOW · No issues

  • Runlayer6mo

    8/24 files flagged

  • ZeroLeaks5mo

    Score: 93/100 · 2 sections analyzed

Signed by skilld at fc8116b. This ties the file your Agent reads to that commit on GitHub. It does not review the instructions.

Last checked against GitHub 2 weeks ago.

Activeupdated 2 months ago
  • Next.js
  • Nuxt
  • sanity
  • groq
  • schema
  • typegen
  • portable-text
  • visual-editing
  • astro
  • remix
  • sveltekit
  • migrations
  • localization
  • cms

README badge

README badge for sanity-io/agent-toolkit/sanity-best-practices

Provides guidelines and reference materials for Sanity schema design, GROQ queries, TypeGen, Visual Editing, Portable Text, Studio structure, localization, migrations, Sanity Functions, and framework integrations including Next.js, Nuxt, Astro, Remix, SvelteKit, Angular, and Hydrogen. Use this skill when setting up Sanity projects, designing content models, writing queries, implementing live preview, or integrating Sanity with a frontend framework.

Generated from the current SKILL.md.

Does this skill cover framework integrations like Next.js, Nuxt, and Astro?
Yes. The skill includes integration guides for Next.js, Nuxt, Astro, Remix, SvelteKit, Angular, Hydrogen, and standalone Studio patterns.
What does this skill cover for GROQ queries?
The skill provides GROQ query patterns, type safety approaches, and performance optimization guidelines, with reference materials for detailed examples.
Does this skill include guidance on Sanity Functions and event automation?
Yes. The skill covers Sanity Functions for automating content workflows, including documentEventHandler and defineDocumentFunction patterns.
Can I use this skill for schema design and content modeling?
Yes. The skill provides schema design best practices, field definitions, validation patterns, and content modeling guidance for different use cases.
Does this cover Visual Editing and live preview setup?
Yes. The skill includes guidance on the Presentation Tool, Stega, overlays, and live preview configuration for frontend frameworks.

Generated from the current SKILL.md. These answers refresh after source changes.