---
title: "sanity-io/agent-toolkit"
description: "Collection of resources to help AI agents build better with Sanity."
source: https://github.com/sanity-io/agent-toolkit
ref: main
license: MIT
licenseName: "MIT License"
canonical: https://skillsdocs.com/sanity-io/agent-toolkit
base: https://github.com/sanity-io/agent-toolkit/blob/main/
provenance: authored
chapters: 8
inlined: 8
withheld: 0
words: 4982
updated: 2026-09-16T18:54:38Z
generator: "Skills Docs"
---

> **sanity-io/agent-toolkit** — every Agent Skill in this repository, inlined verbatim.
>
> Canonical HTML: https://skillsdocs.com/sanity-io/agent-toolkit
> Per-skill Markdown: https://skillsdocs.com/sanity-io/agent-toolkit/<skill>.md
> Machine manifest: https://skillsdocs.com/sanity-io/agent-toolkit/.well-known/agent-skills/index.json
> JSON: https://skillsdocs.com/api/v1/books/sanity-io/agent-toolkit
> Install: `npx skills add sanity-io/agent-toolkit`
> Upstream: https://github.com/sanity-io/agent-toolkit @ `main`
> Licence: MIT
>
> Content is mirrored from GitHub and © its authors, served unmodified. Takedown: https://github.com/DreambaseAI/skillsdocs/issues/new?labels=takedown&title=Takedown+request

# sanity-io/agent-toolkit

Collection of resources to help AI agents build better with Sanity.

- **Skills:** 8
- **Inlined:** 8 (licence detected)
- **Words:** 4,982
- **Reading time:** 23 min
- **Stars:** 186

## Table of contents

1. [content-experimentation-best-practices](https://skillsdocs.com/sanity-io/agent-toolkit/content-experimentation-best-practices.md) — Content experimentation and A/B testing guidance covering experiment design, hypotheses, metrics, sample size, statistical foundations, CMS-managed variants, a…
2. [content-modeling-best-practices](https://skillsdocs.com/sanity-io/agent-toolkit/content-modeling-best-practices.md) — Structured content modeling guidance for schema design, content architecture, content reuse, references versus embedded objects, separation of concerns, and ta…
3. [portable-text-conversion](https://skillsdocs.com/sanity-io/agent-toolkit/portable-text-conversion.md) — Convert HTML and Markdown content into Portable Text blocks for Sanity. Use when migrating content from legacy CMSs, importing HTML or Markdown into Sanity, bu…
4. [portable-text-serialization](https://skillsdocs.com/sanity-io/agent-toolkit/portable-text-serialization.md) — Render and serialize Portable Text to React, Svelte, Vue, Astro, HTML, Markdown, and plain text. Use when implementing Portable Text rendering in any frontend…
5. [sanity-best-practices](https://skillsdocs.com/sanity-io/agent-toolkit/sanity-best-practices.md) — Sanity development best practices for schema design, GROQ queries, TypeGen, Visual Editing, images, Portable Text, Studio structure, localization, migrations,…
6. [sanity-migration](https://skillsdocs.com/sanity-io/agent-toolkit/sanity-migration.md) — Plans, implements, and reviews migrations from other CMSes and content systems into Sanity. Use when migrating or replatforming to Sanity from AEM, Adobe Exper…
7. [sanity-studio-upgrade](https://skillsdocs.com/sanity-io/agent-toolkit/sanity-studio-upgrade.md) — Produces a tailored Sanity Studio upgrade plan by inspecting the repository's installed versions, config, and source, then reporting only the breaking changes…
8. [seo-aeo-best-practices](https://skillsdocs.com/sanity-io/agent-toolkit/seo-aeo-best-practices.md) — SEO and AEO best practices for metadata, Open Graph, sitemaps, robots.txt, hreflang, JSON-LD structured data, EEAT, and content optimized for search engines an…


## Front matter

_The repository README, verbatim except that relative links are resolved against https://github.com/sanity-io/agent-toolkit/blob/main/._

<p align="center">
  <a href="https://sanity.io">
    <img src="https://cdn.sanity.io/images/3do82whm/next/d6cf401d52c33b7a5a354a14ab7de94dea2f0c02-192x192.svg" />
  </a>
  <h1 align="center">Sanity Agent Toolkit</h1>
</p>

Collection of resources to help AI agents build better with [Sanity](https://www.sanity.io). Supports Cursor, Claude Code, Codex, VS Code, Lovable, v0, Replit, OpenCode, and any other editor/agent compatible with MCP or [Agent Skills](https://agentskills.io).

---

## Features

- **MCP server:** Direct access to your Sanity projects (content, datasets, releases, schemas) and agent rules.
- **Agent skills:** Comprehensive best practices skills for Sanity development, content modeling, SEO/AEO, and experimentation. Includes 21 integration/topic guides and 26 focused best-practice rules.
- **Agent Plugin:** `plugin.json`, `skills/`, and `mcp.json` components for any Agent Plugins-compatible client.
- **Claude Code plugin:** MCP server, agent skills, and slash commands for Claude Code users. Available on the [official Anthropic plugin marketplace](https://claude.com/plugins/sanity).
- **Cursor plugin:** MCP server, agent skills, and commands on the [Cursor Marketplace](https://cursor.com/marketplace/sanity).
- **Codex plugin:** MCP server and agent skills for [OpenAI Codex](https://developers.openai.com/codex) users.

---

## Get started

Choose your path based on how you want agents to work with Sanity:

1. **MCP server** — Give your agent always up-to-date rules and full access to your Sanity projects. No local files to maintain. Works with Cursor, VS Code, Claude Code, Lovable, v0, Replit, OpenCode, and other MCP-compatible clients.
2. **Agent skills** — Install best practices skills for Sanity, content modeling, SEO/AEO, and experimentation. Works with Cursor, Claude Code, and any [Agent Skills](https://agentskills.io)-compatible agent.
3. **Plugin** — Install the Sanity plugin for Cursor, Claude Code, or any Agent Plugins-compatible client. Bundles MCP server, agent skills, and commands.
4. **Manual installation** — Copy the skill references locally for offline use. You'll need to update them yourself.

### Option 1: Install MCP server (recommended)

Give agents direct access to Sanity projects and always up-to-date agent rules via the MCP server.

#### Quick install via Sanity CLI

Run in terminal to detect and configure MCP for Cursor, Claude Code and VS Code automatically:

```bash
npx sanity@latest mcp configure
```

Uses your logged-in CLI user for authentication — no manual tokens or OAuth needed.

#### Client-specific instructions

<details>
<summary><strong>Cursor</strong></summary>

One-click install:<br>
[![Install MCP Server](https://cursor.com/deeplink/mcp-install-dark.svg)](https://cursor.com/en-US/install-mcp?name=Sanity&config=eyJ0eXBlIjoiaHR0cCIsInVybCI6Imh0dHBzOi8vbWNwLnNhbml0eS5pbyJ9)

Or manually: Open **Command Palette** (`Cmd+Shift+P` / `Ctrl+Shift+P`) → **View: Open MCP Settings** → **+ New MCP Server** → add to `mcp.json`:
```json
{
  "mcpServers": {
    "Sanity": {
      "type": "http",
      "url": "https://mcp.sanity.io"
    }
  }
}
```
</details>

<details>
<summary><strong>Claude Code</strong></summary>

Run in terminal. Authenticate with OAuth on next launch:
```bash
claude mcp add Sanity -t http https://mcp.sanity.io --scope user
```
</details>

<details>
<summary><strong>Codex</strong></summary>

Run in terminal. Authenticate with OAuth on next launch:
```bash
codex mcp add Sanity --url https://mcp.sanity.io
```

Or manually add to `~/.codex/config.toml`:
```toml
[mcp_servers.Sanity]
url = "https://mcp.sanity.io"
```
</details>

<details>
<summary><strong>VS Code</strong></summary>

Open **Command Palette** (`Cmd+Shift+P` / `Ctrl+Shift+P`) → **MCP: Open User Configuration** → add:
```json
{
  "servers": {
    "Sanity": {
      "type": "http",
      "url": "https://mcp.sanity.io"
    }
  }
}
```
</details>

<details>
<summary><strong>Lovable</strong></summary>

Sanity is available as a prebuilt chat connector in Lovable:

1. Open **Connectors** → **Chat connectors**
2. Select **Sanity**
3. Click **Connect** and sign in to authorize your Sanity account

In your next prompt, reference your Sanity project or ask the agent to read your schema.

See the [Lovable MCP documentation](https://docs.lovable.dev/integrations/mcp-servers) or [Sanity + Lovable guide](https://lovable.dev/connect/sanity) for more details.
</details>

<details>
<summary><strong>v0</strong></summary>

In the prompt input field, click **Prompt Tools** → **MCPs** → **Add New** → Select **Sanity** → **Authorize** → Authenticate with OAuth.
</details>

<details>
<summary><strong>Replit</strong></summary>

Go to [Integrations Page](https://replit.com/integrations) → scroll to **MCP Servers for Replit Agent** → **Add MCP server** → Enter `Sanity` as name and `https://mcp.sanity.io` as Server URL → **Test & Save** → Authenticate with OAuth.
</details>

<details>
<summary><strong>OpenCode</strong></summary>

Add to your `opencode.json`:
```json
{
  "$schema": "https://opencode.ai/config.json",
  "mcp": {
    "sanity": {
      "type": "remote",
      "url": "https://mcp.sanity.io",
      "oauth": {}
    }
  }
}
```
Then run: `opencode mcp auth sanity`
</details>

<details>
<summary><strong>Other clients</strong></summary>

For any MCP-compatible client, add `https://mcp.sanity.io` as the server URL.

If your client doesn't support remote MCP servers, use a proxy like `mcp-remote`:
```json
{
  "mcpServers": {
    "Sanity": {
      "command": "npx",
      "args": ["mcp-remote", "https://mcp.sanity.io", "--transport", "http-only"]
    }
  }
}
```
</details>

<br />

#### Authorization and troubleshooting

Manual MCP configuration uses OAuth by default. You can use token auth instead by setting an `Authorization: Bearer <token>` header in the MCP config. If authentication fails after CLI setup, rerun `npx sanity@latest mcp configure` and restart your MCP client. For OAuth reset issues, Cursor provides **Cursor: Clear All MCP Tokens** and VS Code provides **Authentication: Remove Dynamic Authentication Providers**.

See the [Sanity MCP docs](https://www.sanity.io/docs/ai/mcp-server) for authorization options and troubleshooting.

### Option 2: Install Agent Skills

Install best practices skills that work with any [Agent Skills](https://agentskills.io)-compatible agent.

```bash
npx skills add sanity-io/agent-toolkit
```

See [Option 3](#option-3-install-plugin) for plugin installation.

### Option 3: Install plugin

Install the Sanity plugin to get MCP server, agent skills, and commands.
Available on the [Claude Code marketplace](https://claude.com/plugins/sanity), [Cursor Marketplace](https://cursor.com/marketplace/sanity), or from this repo as an [Agent Plugin](https://agent-plugins.org/).

#### Claude Code

The Sanity plugin is listed on the [official Anthropic plugin marketplace](https://claude.com/plugins/sanity). The official marketplace (`claude-plugins-official`) is pre-registered when you start Claude Code — you do not need to add a custom marketplace.

Install from Claude Code:

```
/plugin install sanity@claude-plugins-official
```

If the plugin is not found, refresh the marketplace catalog and retry:

```
/plugin marketplace update claude-plugins-official
```

Then run `/reload-plugins` to activate without restarting.

**Alternative: interactive install**

1. Run `/plugin` and open the **Discover** tab
2. Search for **Sanity**
3. Review what the plugin will install — commands, skills, hooks, and MCP servers — before confirming ([Anthropic recommends reviewing plugin permissions and source before installing](https://code.claude.com/docs/en/discover-plugins#install-plugins))
4. Choose an installation scope:
   - **User** (default): all projects on this machine
   - **Project**: shared with collaborators via `.claude/settings.json`
   - **Local**: this repository only
5. Run `/reload-plugins` to activate without restarting

**Verify installation:** Ask Claude Code: "which skills do you have access to?"

You should see the Sanity skills listed.

**Start using:** Use natural language and skills activate automatically:

> Help me create a blog post schema in Sanity

> Review my GROQ query and Next.js Visual Editing setup

Or run `/sanity` to explore all capabilities.

#### Cursor

Install from the [Cursor Marketplace](https://cursor.com/marketplace/sanity) by running this in Cursor chat:

```
/add-plugin sanity
```

**Verify installation:** Ask Cursor: "which skills do you have access to?"

You should see the Sanity skills listed.

**Start using:** Use natural language and skills activate automatically:

> Help me create a blog post schema in Sanity

> Review my GROQ query and Next.js Visual Editing setup

#### Codex

1. Add the Sanity marketplace:

```bash
codex plugin marketplace add sanity-io/agent-toolkit
```

2. Install the plugin from Codex's plugin directory (select the **Sanity Agent Toolkit** marketplace, then install **Sanity**).

3. Restart Codex. Verify by asking: "which skills do you have access to?" — you should see the Sanity skills listed.

### Option 4: Manual installation

Install the skill references locally to teach your editor Sanity best practices:

1. Copy `skills/sanity-best-practices/` to your project.
2. (Recommended) Copy `AGENTS.md` to your project root to act as a knowledge router.

---

## Capabilities

### MCP tools

With MCP connected, your AI can use tools like:
- `query_documents` — run GROQ queries directly
- `create_documents` — create draft documents from structured content, or version documents when a release ID is provided
- `patch_documents` — surgical edits to existing documents; published documents are edited by creating or updating drafts
- `publish_documents` / `unpublish_documents` — manage document lifecycle
- `deploy_schema` / `get_schema` — deploy MCP-managed schemas and inspect deployed schemas
- `deploy_studio` — deploy a hosted Studio bound to an MCP-managed schema
- `create_release` / `list_releases` — create and inspect Content Releases
- `create_version` — create version documents for releases
- `generate_image` / `transform_image` — AI image generation and editing
- `whoami` — verify the authenticated Sanity user
- `get_project_studios` — list Studio applications linked to a project
- `search_docs` / `read_docs` — search and read Sanity documentation
- `list_sanity_rules` / `get_sanity_rules` — load agent rules on demand
- `give_feedback` — report MCP tool errors, missing capabilities, confusing output, or documentation issues

MCP-managed schemas are resolved before Studio-deployed and legacy schemas. If you deploy schema changes with `deploy_schema`, redeploy any matching MCP-managed Studio with `deploy_studio` so it picks up the latest schema. `generate_image`, `transform_image`, and `create_version` with an `instruction` consume Sanity AI credits.

See the [full list of available tools](https://www.sanity.io/docs/ai/mcp-server#available-tools).

### Agent skills

Best practices skills that agents like Claude Code, Cursor, GitHub Copilot, etc. can discover and use automatically. Skills follow the [Agent Skills](https://agentskills.io) format. See [Option 2](#option-2-install-agent-skills) for installation.

| Skill | Description |
| :--- | :--- |
| **sanity-best-practices** | GROQ performance, schema design, Visual Editing, images, Portable Text, Studio, TypeGen, localization, migrations, and framework integration guides |
| **content-modeling-best-practices** | Structured content principles: separation of concerns, references vs embedding, content reuse |
| **seo-aeo-best-practices** | SEO/AEO with EEAT principles, structured data (JSON-LD), technical SEO patterns |
| **content-experimentation-best-practices** | A/B testing methodology, statistical foundations, experiment design |

### Getting started flow

The onboarding guide follows three phases:

1. **Studio & Schema** — Set up Sanity Studio and define your content model
2. **Content** — Import existing content or generate placeholder content via MCP
3. **Frontend** — Integrate with your application (framework-specific)

Just say: "Get started with Sanity" to begin.

### Slash commands (Claude Code)

| Command | What it does |
| :--- | :--- |
| `/sanity` | List available skills and help topics |
| `/sanity-review` | Review code for Sanity best practices |
| `/typegen` | Run TypeGen and troubleshoot issues |
| `/deploy-schema` | Deploy schema with verification |

---

## Repository structure

> **Note:** The reference files in `skills/sanity-best-practices/references/` are the canonical content for the Sanity MCP server's `list_sanity_rules` / `get_sanity_rules` tools. Each file must have valid `name` and `description` frontmatter — rule names are derived from filenames (e.g., `nextjs.md` → `nextjs`).

```text
sanity-io/agent-toolkit/
├── AGENTS.md                      # Knowledge router & agent behavior
├── README.md                      # This file
├── plugin.json                    # Portable Agent Plugins v1 manifest
├── mcp.json                       # Portable Agent Plugins v1 MCP configuration
├── .agents/plugins/               # Codex marketplace
│   └── marketplace.json           # Codex marketplace metadata
├── .claude-plugin/                # Claude Code plugin configuration (distributed via claude-plugins-official)
│   ├── plugin.json                # Plugin manifest (name: sanity)
│   └── marketplace.json           # Marketplace manifest for repo-based discovery
├── .codex-plugin/                 # Codex plugin configuration
│   └── plugin.json                # Codex plugin manifest
├── .cursor-plugin/                # Cursor plugin configuration (distributed via cursor.com/marketplace)
│   ├── marketplace.json           # Cursor marketplace metadata
│   └── plugin.json                # Per-plugin manifest
├── .mcp.json                      # Legacy client MCP compatibility configuration
├── assets/                        # Plugin branding
│   └── logo.svg                   # Sanity logo for marketplace display
├── commands/                      # Agent commands
│   ├── sanity.md                  # /sanity help
│   ├── sanity-review.md           # /sanity-review
│   ├── typegen.md                 # /typegen
│   └── deploy-schema.md           # /deploy-schema
├── scripts/                       # Validation and CI scripts
│   ├── validate-agent-plugin.mjs  # Validate manifests against the v1 JSON Schemas
│   └── validate-cursor-plugin.mjs # Cursor plugin validator
└── skills/                        # Agent skills (agentskills.io format)
    ├── sanity-best-practices/     # Comprehensive Sanity skill
    │   ├── SKILL.md               # Skill definition and quick reference
    │   └── references/            # Canonical content (22 guides)
    │       ├── get-started.md     # Onboarding guide
    │       ├── nextjs.md          # Next.js integration
    │       ├── groq.md            # GROQ patterns & performance
    │       ├── schema.md          # Schema design & validation
    │       └── ...                # See SKILL.md for full index
    ├── content-modeling-best-practices/      # Modeling guidance + topic references
    ├── seo-aeo-best-practices/               # SEO/AEO guidance + topic references
    └── content-experimentation-best-practices/ # Experiment design + stats references
```

All skills use `references/` for detailed content loaded on demand. The `sanity-best-practices` references are also the canonical source for the MCP server's Sanity rules.

---

## Resources

- [Create Sanity account](https://www.sanity.io/get-started)
- [Sanity documentation](https://www.sanity.io/docs)
- [GROQ language reference](https://www.sanity.io/docs/groq)
- [Visual Editing guide](https://www.sanity.io/docs/visual-editing)
- [Sanity TypeGen](https://www.sanity.io/docs/sanity-typegen)
- [MCP server docs](https://www.sanity.io/docs/ai/mcp-server)
- [Agent Plugins specification](https://agent-plugins.org/specification)
- [Blueprints Infrastructure as Code](https://www.sanity.io/docs/compute-and-ai/blueprints)

---

## Contributing

Found a better pattern? Missing a framework or best practice? Read the [contributing guide](https://github.com/sanity-io/agent-toolkit/blob/main/CONTRIBUTING.md) for how skills work and what makes a good contribution, then:

1. Fork the repo.
2. Install dependencies with `npm install`.
3. Make your changes in `skills/<skill-name>/`.
4. Run `npm run validate:all` to check skill and plugin validity.
5. Submit a PR.

---

## Support

- [Sanity Community (Discord)](https://www.sanity.io/community/join)
- [GitHub issues](https://github.com/sanity-io/agent-toolkit/issues)

---

**License:** MIT

---

<!-- chapter:begin slug=content-experimentation-best-practices position=1 -->

## 1. content-experimentation-best-practices

- **Source:** https://github.com/sanity-io/agent-toolkit/blob/main/skills/content-experimentation-best-practices/SKILL.md
- **Raw:** https://raw.githubusercontent.com/sanity-io/agent-toolkit/main/skills/content-experimentation-best-practices/SKILL.md
- **Markdown:** https://skillsdocs.com/sanity-io/agent-toolkit/content-experimentation-best-practices.md
- **Licence:** MIT — https://spdx.org/licenses/MIT.html

Bundled files (4), referenced from this skill's directory:
  - `references/cms-integration.md` — https://raw.githubusercontent.com/sanity-io/agent-toolkit/main/skills/content-experimentation-best-practices/references/cms-integration.md
  - `references/common-pitfalls.md` — https://raw.githubusercontent.com/sanity-io/agent-toolkit/main/skills/content-experimentation-best-practices/references/common-pitfalls.md
  - `references/experiment-design.md` — https://raw.githubusercontent.com/sanity-io/agent-toolkit/main/skills/content-experimentation-best-practices/references/experiment-design.md
  - `references/statistical-foundations.md` — https://raw.githubusercontent.com/sanity-io/agent-toolkit/main/skills/content-experimentation-best-practices/references/statistical-foundations.md

<!-- Verbatim upstream SKILL.md follows, YAML frontmatter included. -->

---
name: content-experimentation-best-practices
description: Content experimentation and A/B testing guidance covering experiment design, hypotheses, metrics, sample size, statistical foundations, CMS-managed variants, and common analysis pitfalls. Use this skill when planning experiments, setting up variants, choosing success metrics, interpreting statistical results, or building experimentation workflows in a CMS or frontend stack.
---

# Content Experimentation Best Practices

Principles and patterns for running effective content experiments to improve conversion rates, engagement, and user experience.

## When to Apply

Reference these guidelines when:
- Setting up A/B or multivariate testing infrastructure
- Designing experiments for content changes
- Analyzing and interpreting test results
- Building CMS integrations for experimentation
- Deciding what to test and how

## Core Concepts

### A/B Testing
Comparing two variants (A vs B) to determine which performs better.

### Multivariate Testing
Testing multiple variables simultaneously to find optimal combinations.

### Statistical Significance
The confidence level that results aren't due to random chance.

### Experimentation Culture
Making decisions based on data rather than opinions (HiPPO avoidance).

## References

Start with the reference that matches the current problem, such as design, statistics, CMS integration, or pitfalls. See `references/` for detailed guidance:
- `references/experiment-design.md` — Hypothesis framework, metrics, sample size, and what to test
- `references/statistical-foundations.md` — p-values, confidence intervals, power analysis, Bayesian methods
- `references/cms-integration.md` — CMS-managed variants, field-level variants, external platforms
- `references/common-pitfalls.md` — 17 common mistakes across statistics, design, execution, and interpretation

<!-- chapter:end slug=content-experimentation-best-practices -->

---

<!-- chapter:begin slug=content-modeling-best-practices position=2 -->

## 2. content-modeling-best-practices

- **Source:** https://github.com/sanity-io/agent-toolkit/blob/main/skills/content-modeling-best-practices/SKILL.md
- **Raw:** https://raw.githubusercontent.com/sanity-io/agent-toolkit/main/skills/content-modeling-best-practices/SKILL.md
- **Markdown:** https://skillsdocs.com/sanity-io/agent-toolkit/content-modeling-best-practices.md
- **Licence:** MIT — https://spdx.org/licenses/MIT.html

Bundled files (4), referenced from this skill's directory:
  - `references/content-reuse.md` — https://raw.githubusercontent.com/sanity-io/agent-toolkit/main/skills/content-modeling-best-practices/references/content-reuse.md
  - `references/reference-vs-embedding.md` — https://raw.githubusercontent.com/sanity-io/agent-toolkit/main/skills/content-modeling-best-practices/references/reference-vs-embedding.md
  - `references/separation-of-concerns.md` — https://raw.githubusercontent.com/sanity-io/agent-toolkit/main/skills/content-modeling-best-practices/references/separation-of-concerns.md
  - `references/taxonomy-classification.md` — https://raw.githubusercontent.com/sanity-io/agent-toolkit/main/skills/content-modeling-best-practices/references/taxonomy-classification.md

<!-- Verbatim upstream SKILL.md follows, YAML frontmatter included. -->

---
name: content-modeling-best-practices
description: Structured content modeling guidance for schema design, content architecture, content reuse, references versus embedded objects, separation of concerns, and taxonomies across Sanity and other headless CMSes. Use this skill when designing or refactoring content types, deciding field shapes, debating reusable versus nested content, planning omnichannel content models, or reviewing whether a schema is too page-shaped or presentation-driven.
---

# Content Modeling Best Practices

Principles for designing structured content that's flexible, reusable, and maintainable. These concepts apply to any headless CMS but include Sanity-specific implementation notes.

## When to Apply

Reference these guidelines when:
- Starting a new project and designing the content model
- Evaluating whether content should be structured or free-form
- Deciding between references and embedded content
- Planning for multi-channel content delivery
- Refactoring existing content structures

## Core Principles

1. **Content is data, not pages** — Structure content for meaning, not presentation
2. **Single source of truth** — Avoid content duplication
3. **Future-proof** — Design for channels that don't exist yet
4. **Editor-centric** — Optimize for the people creating content

## References

Start with the reference that matches the modeling decision in front of you, instead of loading every topic at once. See `references/` for detailed guidance on specific topics:
- `references/separation-of-concerns.md` — Separating content from presentation
- `references/reference-vs-embedding.md` — When to use references vs embedded objects
- `references/content-reuse.md` — Content reuse patterns and the reuse spectrum
- `references/taxonomy-classification.md` — Flat, hierarchical, and faceted classification

<!-- chapter:end slug=content-modeling-best-practices -->

---

<!-- chapter:begin slug=portable-text-conversion position=3 -->

## 3. portable-text-conversion

- **Source:** https://github.com/sanity-io/agent-toolkit/blob/main/skills/portable-text-conversion/SKILL.md
- **Raw:** https://raw.githubusercontent.com/sanity-io/agent-toolkit/main/skills/portable-text-conversion/SKILL.md
- **Markdown:** https://skillsdocs.com/sanity-io/agent-toolkit/portable-text-conversion.md
- **Licence:** MIT — https://spdx.org/licenses/MIT.html

Bundled files (3), referenced from this skill's directory:
  - `rules/html-to-pt.md` — https://raw.githubusercontent.com/sanity-io/agent-toolkit/main/skills/portable-text-conversion/rules/html-to-pt.md
  - `rules/manual-construction.md` — https://raw.githubusercontent.com/sanity-io/agent-toolkit/main/skills/portable-text-conversion/rules/manual-construction.md
  - `rules/markdown-to-pt.md` — https://raw.githubusercontent.com/sanity-io/agent-toolkit/main/skills/portable-text-conversion/rules/markdown-to-pt.md

<!-- Verbatim upstream SKILL.md follows, YAML frontmatter included. -->

---
name: portable-text-conversion
description: Convert HTML and Markdown content into Portable Text blocks for Sanity. Use when migrating content from legacy CMSs, importing HTML or Markdown into Sanity, building content pipelines that ingest external content, converting rich text between formats, or programmatically creating Portable Text documents. Covers @portabletext/markdown (markdownToPortableText), @portabletext/block-tools (htmlToBlocks), custom deserializers, and the Portable Text specification for manual block construction.
license: MIT
metadata:
  author: sanity
  version: "1.0.0"
---

# Portable Text Conversion

Convert external content (HTML, Markdown) into Portable Text for Sanity. Three main approaches:

1. **`markdownToPortableText`** — Convert Markdown directly using `@portabletext/markdown` (recommended for Markdown)
2. **`htmlToBlocks`** — Parse HTML into PT blocks using `@portabletext/block-tools` (for HTML migration)
3. **Manual construction** — Build PT blocks directly from any source (APIs, databases, etc.)

## Portable Text Specification

Understand the target format before converting. PT is an array of blocks:

```json
[
  {
    "_type": "block",
    "_key": "abc123",
    "style": "normal",
    "children": [
      {"_type": "span", "_key": "def456", "text": "Hello ", "marks": []},
      {"_type": "span", "_key": "ghi789", "text": "world", "marks": ["strong"]}
    ],
    "markDefs": []
  },
  {
    "_type": "block",
    "_key": "jkl012",
    "style": "h2",
    "children": [
      {"_type": "span", "_key": "mno345", "text": "A heading", "marks": []}
    ],
    "markDefs": []
  },
  {
    "_type": "image",
    "_key": "pqr678",
    "asset": {"_type": "reference", "_ref": "image-abc-200x200-png"}
  }
]
```

**Key rules:**
- Every block and span needs `_key` (unique within the array)
- `_type: "block"` is for text blocks; custom types use their own `_type`
- `markDefs` holds annotation data; `marks` on spans reference `markDefs[*]._key` or are decorator strings
- Lists use `listItem` ("bullet" | "number") and `level` (1, 2, 3...) on regular blocks

## Conversion Rules

Read the rule file matching your source format:

- **Markdown → Portable Text**: `rules/markdown-to-pt.md` — `@portabletext/markdown` with `markdownToPortableText` (recommended)
- **HTML → Portable Text**: `rules/html-to-pt.md` — `@portabletext/block-tools` with `htmlToBlocks`
- **Manual PT Construction**: `rules/manual-construction.md` — build blocks programmatically from any source

> **Note:** `@sanity/block-tools` is the legacy package name. Always use `@portabletext/block-tools` for new projects. The API is the same.

<!-- chapter:end slug=portable-text-conversion -->

---

<!-- chapter:begin slug=portable-text-serialization position=4 -->

## 4. portable-text-serialization

- **Source:** https://github.com/sanity-io/agent-toolkit/blob/main/skills/portable-text-serialization/SKILL.md
- **Raw:** https://raw.githubusercontent.com/sanity-io/agent-toolkit/main/skills/portable-text-serialization/SKILL.md
- **Markdown:** https://skillsdocs.com/sanity-io/agent-toolkit/portable-text-serialization.md
- **Licence:** MIT — https://spdx.org/licenses/MIT.html

Bundled files (7), referenced from this skill's directory:
  - `rules/astro.md` — https://raw.githubusercontent.com/sanity-io/agent-toolkit/main/skills/portable-text-serialization/rules/astro.md
  - `rules/html.md` — https://raw.githubusercontent.com/sanity-io/agent-toolkit/main/skills/portable-text-serialization/rules/html.md
  - `rules/markdown.md` — https://raw.githubusercontent.com/sanity-io/agent-toolkit/main/skills/portable-text-serialization/rules/markdown.md
  - `rules/plain-text.md` — https://raw.githubusercontent.com/sanity-io/agent-toolkit/main/skills/portable-text-serialization/rules/plain-text.md
  - `rules/react.md` — https://raw.githubusercontent.com/sanity-io/agent-toolkit/main/skills/portable-text-serialization/rules/react.md
  - `rules/svelte.md` — https://raw.githubusercontent.com/sanity-io/agent-toolkit/main/skills/portable-text-serialization/rules/svelte.md
  - `rules/vue.md` — https://raw.githubusercontent.com/sanity-io/agent-toolkit/main/skills/portable-text-serialization/rules/vue.md

<!-- Verbatim upstream SKILL.md follows, YAML frontmatter included. -->

---
name: portable-text-serialization
description: Render and serialize Portable Text to React, Svelte, Vue, Astro, HTML, Markdown, and plain text. Use when implementing Portable Text rendering in any frontend framework, building custom serializers for non-standard block types, converting Portable Text to HTML strings server-side, converting Portable Text to Markdown, extracting plain text from Portable Text, or troubleshooting rendering issues with marks, blocks, lists, or custom types.
license: MIT
metadata:
  author: sanity
  version: "1.0.0"
---

# Portable Text Serialization

Render Portable Text content across frameworks using the `@portabletext/*` library family. Each library follows the same component-mapping pattern: you provide a `components` object that maps PT node types to framework-specific renderers.

## Portable Text Structure (Quick Reference)

PT is an array of blocks. Each block has `_type`, optional `style`, `children` (spans), `markDefs`, `listItem`, and `level`.

```
Root array
├── block (_type: "block")
│   ├── style: "normal" | "h1" | "h2" | "blockquote" | ...
│   ├── children: [span, span, ...]
│   │   └── span: { _type: "span", text: "...", marks: ["strong", "<markDefKey>"] }
│   ├── markDefs: [{ _key, _type: "link", href: "..." }, ...]
│   ├── listItem: "bullet" | "number" (optional)
│   └── level: 1, 2, 3... (optional, for nested lists)
├── custom block (_type: "image" | "code" | any custom type)
└── ...more blocks
```

**Marks** come in two forms:
- **Decorators**: string values in `marks[]` like `"strong"`, `"em"`, `"underline"`, `"code"`
- **Annotations**: keys in `marks[]` referencing entries in `markDefs[]` (e.g., links, internal references)

## Component Mapping Pattern (All Frameworks)

Every `@portabletext/*` library accepts a `components` object with these keys:

| Key | Renders | Props/Data |
|-----|---------|------------|
| `types` | Custom block/inline types (image, code, CTA) | `value` (the block data) |
| `marks` | Decorators + annotations | `children` + `value` (mark data) |
| `block` | Block styles (h1, normal, blockquote) | `children` |
| `list` | List wrappers (ul, ol) | `children` |
| `listItem` | List items | `children` |
| `hardBreak` | Line breaks within a block | — |

## Framework-Specific Rules

Read the rule file matching your framework:

- **React / Next.js**: `rules/react.md` — `@portabletext/react` or `next-sanity`
- **Svelte / SvelteKit**: `rules/svelte.md` — `@portabletext/svelte`
- **Vue / Nuxt**: `rules/vue.md` — `@portabletext/vue`
- **Astro**: `rules/astro.md` — `astro-portabletext`
- **HTML (server-side)**: `rules/html.md` — `@portabletext/to-html`
- **Markdown**: `rules/markdown.md` — `@portabletext/markdown`
- **Plain text extraction**: `rules/plain-text.md` — `@portabletext/toolkit`

### Additional Community Serializers

These are listed on [portabletext.org](https://www.portabletext.org/integrations/serializers/) but don't have dedicated rule files:

| Target | Package |
|--------|---------|
| React Native | `@portabletext/react-native-portabletext` |
| React PDF | `@portabletext/react-pdf-portabletext` |
| Solid | `solid-portabletext` |
| Qwik | `portabletext-qwik` |
| Shopify Liquid | `portable-text-to-liquid` |
| PHP | `sanity-php` (SanityBlockContent class) |
| Python | `portabletext-html` |
| C# / .NET | `dotnet-portable-text` |
| Dart / Flutter | `flutter_sanity_portable_text` |

## Common Patterns (All Frameworks)

### Custom Types Need Explicit Components

PT renderers only handle standard blocks by default. Custom types (`image`, `code`, `callToAction`, etc.) require explicit component mappings — they won't render otherwise.

### Keep Components Object Stable

In React/Vue, define `components` outside the render function or memoize it. Recreating on every render causes unnecessary re-renders.

### Handle Missing Components Gracefully

All libraries accept `onMissingComponent` to control behavior when encountering unknown types:
- `false` — suppress warnings
- Custom function — log or report

### Querying PT with GROQ

Always expand references inside custom blocks:

```groq
body[]{
  ...,
  _type == "image" => {
    ...,
    asset->
  },
  markDefs[]{
    ...,
    _type == "internalLink" => {
      ...,
      "slug": @.reference->slug.current
    }
  }
}
```

<!-- chapter:end slug=portable-text-serialization -->

---

<!-- chapter:begin slug=sanity-best-practices position=5 -->

## 5. sanity-best-practices

- **Source:** https://github.com/sanity-io/agent-toolkit/blob/main/skills/sanity-best-practices/SKILL.md
- **Raw:** https://raw.githubusercontent.com/sanity-io/agent-toolkit/main/skills/sanity-best-practices/SKILL.md
- **Markdown:** https://skillsdocs.com/sanity-io/agent-toolkit/sanity-best-practices.md
- **Licence:** MIT — https://spdx.org/licenses/MIT.html

Bundled files (24), referenced from this skill's directory:
  - `references/angular.md` — https://raw.githubusercontent.com/sanity-io/agent-toolkit/main/skills/sanity-best-practices/references/angular.md
  - `references/app-sdk.md` — https://raw.githubusercontent.com/sanity-io/agent-toolkit/main/skills/sanity-best-practices/references/app-sdk.md
  - `references/astro.md` — https://raw.githubusercontent.com/sanity-io/agent-toolkit/main/skills/sanity-best-practices/references/astro.md
  - `references/blueprints.md` — https://raw.githubusercontent.com/sanity-io/agent-toolkit/main/skills/sanity-best-practices/references/blueprints.md
  - `references/functions.md` — https://raw.githubusercontent.com/sanity-io/agent-toolkit/main/skills/sanity-best-practices/references/functions.md
  - `references/get-started.md` — https://raw.githubusercontent.com/sanity-io/agent-toolkit/main/skills/sanity-best-practices/references/get-started.md
  - `references/groq.md` — https://raw.githubusercontent.com/sanity-io/agent-toolkit/main/skills/sanity-best-practices/references/groq.md
  - `references/hydrogen.md` — https://raw.githubusercontent.com/sanity-io/agent-toolkit/main/skills/sanity-best-practices/references/hydrogen.md
  - `references/image.md` — https://raw.githubusercontent.com/sanity-io/agent-toolkit/main/skills/sanity-best-practices/references/image.md
  - `references/localization.md` — https://raw.githubusercontent.com/sanity-io/agent-toolkit/main/skills/sanity-best-practices/references/localization.md
  - `references/migration-html-import.md` — https://raw.githubusercontent.com/sanity-io/agent-toolkit/main/skills/sanity-best-practices/references/migration-html-import.md
  - `references/migration.md` — https://raw.githubusercontent.com/sanity-io/agent-toolkit/main/skills/sanity-best-practices/references/migration.md
  - `references/nextjs.md` — https://raw.githubusercontent.com/sanity-io/agent-toolkit/main/skills/sanity-best-practices/references/nextjs.md
  - `references/nuxt.md` — https://raw.githubusercontent.com/sanity-io/agent-toolkit/main/skills/sanity-best-practices/references/nuxt.md
  - `references/page-builder.md` — https://raw.githubusercontent.com/sanity-io/agent-toolkit/main/skills/sanity-best-practices/references/page-builder.md
  - `references/portable-text.md` — https://raw.githubusercontent.com/sanity-io/agent-toolkit/main/skills/sanity-best-practices/references/portable-text.md
  - `references/project-structure.md` — https://raw.githubusercontent.com/sanity-io/agent-toolkit/main/skills/sanity-best-practices/references/project-structure.md
  - `references/remix.md` — https://raw.githubusercontent.com/sanity-io/agent-toolkit/main/skills/sanity-best-practices/references/remix.md
  - `references/schema.md` — https://raw.githubusercontent.com/sanity-io/agent-toolkit/main/skills/sanity-best-practices/references/schema.md
  - `references/seo.md` — https://raw.githubusercontent.com/sanity-io/agent-toolkit/main/skills/sanity-best-practices/references/seo.md
  - `references/studio-structure.md` — https://raw.githubusercontent.com/sanity-io/agent-toolkit/main/skills/sanity-best-practices/references/studio-structure.md
  - `references/svelte.md` — https://raw.githubusercontent.com/sanity-io/agent-toolkit/main/skills/sanity-best-practices/references/svelte.md
  - `references/typegen.md` — https://raw.githubusercontent.com/sanity-io/agent-toolkit/main/skills/sanity-best-practices/references/typegen.md
  - `references/visual-editing.md` — https://raw.githubusercontent.com/sanity-io/agent-toolkit/main/skills/sanity-best-practices/references/visual-editing.md

<!-- Verbatim upstream SKILL.md follows, YAML frontmatter included. -->

---
name: sanity-best-practices
description: 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.
---

# 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
- 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
- `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

<!-- chapter:end slug=sanity-best-practices -->

---

<!-- chapter:begin slug=sanity-migration position=6 -->

## 6. sanity-migration

- **Source:** https://github.com/sanity-io/agent-toolkit/blob/main/skills/sanity-migration/SKILL.md
- **Raw:** https://raw.githubusercontent.com/sanity-io/agent-toolkit/main/skills/sanity-migration/SKILL.md
- **Markdown:** https://skillsdocs.com/sanity-io/agent-toolkit/sanity-migration.md
- **Licence:** MIT — https://spdx.org/licenses/MIT.html

Bundled files (9), referenced from this skill's directory:
  - `references/aem.md` — https://raw.githubusercontent.com/sanity-io/agent-toolkit/main/skills/sanity-migration/references/aem.md
  - `references/contentful.md` — https://raw.githubusercontent.com/sanity-io/agent-toolkit/main/skills/sanity-migration/references/contentful.md
  - `references/drupal.md` — https://raw.githubusercontent.com/sanity-io/agent-toolkit/main/skills/sanity-migration/references/drupal.md
  - `references/general.md` — https://raw.githubusercontent.com/sanity-io/agent-toolkit/main/skills/sanity-migration/references/general.md
  - `references/markdown.md` — https://raw.githubusercontent.com/sanity-io/agent-toolkit/main/skills/sanity-migration/references/markdown.md
  - `references/payload.md` — https://raw.githubusercontent.com/sanity-io/agent-toolkit/main/skills/sanity-migration/references/payload.md
  - `references/strapi.md` — https://raw.githubusercontent.com/sanity-io/agent-toolkit/main/skills/sanity-migration/references/strapi.md
  - `references/webflow.md` — https://raw.githubusercontent.com/sanity-io/agent-toolkit/main/skills/sanity-migration/references/webflow.md
  - `references/wordpress.md` — https://raw.githubusercontent.com/sanity-io/agent-toolkit/main/skills/sanity-migration/references/wordpress.md

<!-- Verbatim upstream SKILL.md follows, YAML frontmatter included. -->

---
name: sanity-migration
description: Plans, implements, and reviews migrations from other CMSes and content systems into Sanity. Use when migrating or replatforming to Sanity from AEM, Adobe Experience Manager, Contentful, Strapi, Webflow, WordPress, Payload, Drupal, Markdown/MDX/frontmatter files, WXR/XML exports, CMS APIs, database dumps, static HTML, or when designing extraction, transformation, Portable Text conversion, asset migration, redirects, validation, and cutover workflows.
---

# Sanity Migration

Use this skill for CMS-to-Sanity migration work. Treat migration as a content strategy and ETL project, not a blind lift-and-shift.

## Required Workflow

1. Read `references/general.md` first.
2. If the source platform is known, also read its guide:
   - AEM / Adobe Experience Manager: `references/aem.md`
   - Contentful: `references/contentful.md`
   - Strapi: `references/strapi.md`
   - Webflow: `references/webflow.md`
   - WordPress / WXR / Elementor: `references/wordpress.md`
   - Payload: `references/payload.md`
   - Drupal: `references/drupal.md`
   - Markdown / MDX / frontmatter files: `references/markdown.md`
3. Before writing code, produce a short migration plan covering source access, content scope, schema decisions, extraction, transformation, import, validation, redirects, and cutover.
4. Prefer deterministic, repeatable scripts for real migrations. Write and review migration scripts, mappings, and validation checks; do not rely on one-off content operations for large content volumes.

## Deliverables to Produce

For implementation or planning tasks, produce these artifacts or explain why they are not needed:

- Content inventory: source types, counts, locales, status/draft scope, assets, and relationship types.
- Source-to-Sanity mapping: document types, object types, references, Portable Text fields, asset fields, IDs, and skipped content.
- Extraction approach: credentials/access needed, API/export commands, raw snapshot location, and known blind spots.
- Transform/import plan: deterministic IDs, write order, asset handling, rich text conversion, validation, and rerun strategy.
- Cutover plan: delta sync/content freeze, redirects, broken-link checks, SEO metadata, and manual cleanup.

## Defaults

- Use stable document IDs derived from source IDs, slugs, paths, or hashes.
- Use `createOrReplace`, `createIfNotExists`, or `sanity datasets import --replace` so reruns converge.
- Snapshot extracted source data to disk before transforming it.
- Import or create referenced documents before documents that reference them.
- Convert rich text to Portable Text instead of storing raw HTML or Markdown strings.
- Upload assets to Sanity or the Media Library; do not leave production content dependent on legacy CDN URLs.
- Track per-document quality issues and produce a validation summary before cutover.
- Preserve legacy URLs and source IDs for redirects, QA, and future debugging.

## Sanity Guardrails

- Model what content is, not how the old site rendered it.
- Use documents for reusable or independently managed entities; use objects for content owned by one document.
- Use `defineType`, `defineField`, and `defineArrayMember` if authoring Sanity schemas.
- Use image/file fields with uploaded Sanity assets or Media Library assets, not legacy CDN URLs.
- Use Portable Text arrays for rich text and custom blocks; do not store raw HTML as the canonical body.
- Run schema extraction and TypeGen after schema or GROQ query changes when the project uses TypeScript.
- Deploy or apply schema changes before using MCP/content tools against the target dataset.

For deeper Sanity implementation guidance, use `sanity-best-practices` if it is already available. If it is not installed, tell the user they can add it with:

```bash
npx skills add sanity-io/agent-toolkit --skill sanity-best-practices
```

## Stop and Ask

Stop before coding when any of these are unclear:

- Source access path, credentials, export file, or database connection.
- Target Sanity project/dataset or whether a scratch dataset should be used.
- Draft, archived, scheduled, locale, or version history scope.
- Whether media files should be migrated and whether asset URLs/files are accessible.
- Whether the destination schema exists or should be designed as part of the migration.

## Do Not Do This

- Do not create random IDs for source-backed documents.
- Do not fetch-then-create referenced documents; use deterministic IDs and `createIfNotExists`/`createOrReplace`.
- Do not run bulk migrations through MCP content tools when NDJSON or scripts are appropriate.
- Do not flatten locale fallback values into translations unless requested.
- Do not leave TODOs for required media, authors, references, or rich text conversion.
- Do not declare a migration done without count checks, sample checks, reference checks, and route/redirect checks.

## Reference Map

Use `references/general.md` for shared migration principles and the platform references for source-specific extraction routes, modeling traps, and validation checks.

For source systems not explicitly covered, apply `references/general.md` and adapt the closest platform pattern:
- API-first CMSes: start from Contentful, Strapi, or Payload.
- Monolithic/page-builder systems: start from WordPress, Drupal, Webflow, or AEM.
- HTML-heavy exports: start from the WordPress and Webflow rich-text guidance.
- Markdown-first sources: start from `references/markdown.md`.

<!-- chapter:end slug=sanity-migration -->

---

<!-- chapter:begin slug=sanity-studio-upgrade position=7 -->

## 7. sanity-studio-upgrade

- **Source:** https://github.com/sanity-io/agent-toolkit/blob/main/skills/sanity-studio-upgrade/SKILL.md
- **Raw:** https://raw.githubusercontent.com/sanity-io/agent-toolkit/main/skills/sanity-studio-upgrade/SKILL.md
- **Markdown:** https://skillsdocs.com/sanity-io/agent-toolkit/sanity-studio-upgrade.md
- **Licence:** MIT — https://spdx.org/licenses/MIT.html

Bundled files (10), referenced from this skill's directory:
  - `references/boundaries.md` — https://raw.githubusercontent.com/sanity-io/agent-toolkit/main/skills/sanity-studio-upgrade/references/boundaries.md
  - `references/boundary.v4.md` — https://raw.githubusercontent.com/sanity-io/agent-toolkit/main/skills/sanity-studio-upgrade/references/boundary.v4.md
  - `references/boundary.v5.md` — https://raw.githubusercontent.com/sanity-io/agent-toolkit/main/skills/sanity-studio-upgrade/references/boundary.v5.md
  - `references/boundary.v6.md` — https://raw.githubusercontent.com/sanity-io/agent-toolkit/main/skills/sanity-studio-upgrade/references/boundary.v6.md
  - `references/deprecations.md` — https://raw.githubusercontent.com/sanity-io/agent-toolkit/main/skills/sanity-studio-upgrade/references/deprecations.md
  - `references/detect.md` — https://raw.githubusercontent.com/sanity-io/agent-toolkit/main/skills/sanity-studio-upgrade/references/detect.md
  - `references/package-coupling.md` — https://raw.githubusercontent.com/sanity-io/agent-toolkit/main/skills/sanity-studio-upgrade/references/package-coupling.md
  - `references/plugins.md` — https://raw.githubusercontent.com/sanity-io/agent-toolkit/main/skills/sanity-studio-upgrade/references/plugins.md
  - `references/report-template.md` — https://raw.githubusercontent.com/sanity-io/agent-toolkit/main/skills/sanity-studio-upgrade/references/report-template.md
  - `references/version-lookup.md` — https://raw.githubusercontent.com/sanity-io/agent-toolkit/main/skills/sanity-studio-upgrade/references/version-lookup.md

<!-- Verbatim upstream SKILL.md follows, YAML frontmatter included. -->

---
name: sanity-studio-upgrade
description: Produces a tailored Sanity Studio upgrade plan by inspecting the repository's installed versions, config, and source, then reporting only the breaking changes that actually apply. Covers Studio v3 and later; v2 projects are identified and redirected, not planned. Use this skill whenever someone wants to upgrade, migrate, or modernize a Sanity Studio across one or more major versions from v3 onward, asks what will break if they bump the `sanity` package, asks why their Studio broke after an upgrade, or asks how far behind their Studio is. Triggers on "upgrade sanity studio", "migrate our studio to v6", "bump sanity", "what breaks if we upgrade", "our studio is on an old version", "sanity upgrade plan", "is our studio out of date", "we are several majors behind". DO NOT load for upgrading non-Sanity dependencies, for Content Lake `apiVersion` questions, for content or schema migrations that change documents, or for setting up a new Sanity project.
compatibility: Requires network access to the npm registry and sanity.io docs for version and changelog lookups
---

# Sanity Studio upgrade planner

Generate an upgrade plan for the Sanity Studio in the current repository.

The plan's value is **subtraction**. A list of every breaking change between two versions already exists; it is called the changelog, and it is not useful to someone mid-upgrade. This skill produces the much shorter list of changes that apply to *this* repository, plus the questions only a human can answer.

## Three rules that determine whether the output is trustworthy

**1. Never state a version number from memory. Look it up, and confirm it exists.**

Package versions change weekly. Every version in the report must come from either the repository's lockfile (for current state) or a live registry query (for targets). If a lookup is not possible, say so in the report rather than guessing. A plan that names a stale "latest" is worse than no plan, because the reader will act on it.

There is a second, worse version of this failure: a version number that was never published at all, arrived at by assuming one package tracks another's numbering. That one fails at install. Any version you are about to describe as "lockstep" or "matches core" needs an existence check first.

The same applies to API names. An export, an import subpath, or a function signature recalled from memory is a guess, and it is a guess the reader will paste into their editor. Read it from the package's README or `exports` map, or mark it with an inline `// VERIFY:` comment **at the snippet itself**. See section 6 of `plugins.md` and section 8 of `report-template.md`.

**2. Read and report. Never modify.**

Inspect files, run read-only shell commands, query registries. Do not edit `package.json`, config files, or source. The reader decides what to change; a planner that edits code will be turned off. If the user explicitly asks you to perform the upgrade afterward, that is a separate task they have consented to.

**Where the line actually falls**, because a vague version of this rule makes an agent hesitate over commands that are fine:

- **Never**: writing to any file in the repository, installing into it, or mutating a lockfile. `pnpm install`, `npm install`, `npm ci` and `npx` without `--no-install` are all out.
- **Fine**: reading files, and network reads such as `npm view`, a changelog fetch, or `npm pack` **into a temporary directory outside the repository** that you then delete.
- **Recommended to the reader, not run by you**: `pnpm install --lockfile-only` is how a dependency prediction gets settled. It belongs in the report as a command for them to run, with what to expect from it. Do not run it yourself, because it rewrites their lockfile.

The one place this skill writes anything is unpacking a tarball to inspect type declarations, and section 6 of `plugins.md` shows how to keep that outside the project.

**3. Separate what you verified from what you inferred.**

Every finding lands in one of three buckets: confirmed applicable (you found the condition in the repo), needs a human answer (you cannot determine it from code), or not applicable (omit it entirely). Never pad the report with items you could not check. An honest short report beats a comprehensive-looking one.

One consequence is easy to miss and worth stating outright: **a read-only planner has no dependency resolver, so it cannot know what a tree will contain after a change.** Predictions about deduplication belong in the report as expectations with the verification command attached, never as findings. See `package-coupling.md`.

## The verification rules, in one place

These are referred to by number throughout the reference files, so they are stated once here rather than restated in each. Everything above is R1 to R4; the rest are the specific ways those go wrong.

| | Rule | Where it bites |
| --- | --- | --- |
| **R1** | Never state a version from memory. Take current state from the lockfile, targets from a live registry query. | `version-lookup.md` §1 |
| **R2** | Read and report. Nothing in the repository changes, nothing is installed, no lockfile is mutated. | Above, and `plugins.md` §6 |
| **R3** | Separate verified from inferred. Never assert what a dependency tree will contain after a change; a planner has no resolver. | `package-coupling.md` §6, `plugins.md` §5 |
| **R4** | Confirm every recommended version was actually published before it goes in the report. | `version-lookup.md` §3 |
| **R5** | Every "X requires Y" names the manifest it was read from, at the version being installed at that stop. Requirements do not carry backwards across majors. | `version-lookup.md` §6 |
| **R6** | Evaluate a version range, never read it. Anything with a `\|\|`, a `<`, or two comparators in one clause goes through `semver.satisfies` first. | `version-lookup.md` §6 |
| **R7** | A name in an export list is not a working export. Check its declared type and any `@deprecated` tag; `never` means removed. | `plugins.md` §6 |

R6 and R7 exist because both have already produced confident, wrong, specific advice: a Node version reported as unsupported when it was fine, and a required code edit filed under "already satisfied". They are cheap to run and they fail silently when skipped.

## Scope: v3 and later

This skill plans upgrades from Studio v3 onward. **Studio v2 is out of scope and is a hard stop.**

The reason is not that v2 is hard. It is that v2 to v3 is a rewrite of the Studio's configuration and plugin layer rather than a dependency upgrade: the config format changed, the "parts" system that v2 plugins and overrides were built on no longer exists, packages were consolidated, and much of the v2-era plugin ecosystem was never ported. A generated plan for that boundary would be a list of confident-sounding specifics that this skill cannot verify, handed to someone about to spend weeks on the work. That is the worst possible output.

**If you detect v2, stop and report** rather than planning. Step 1 covers detection and what to say. Do not write a plan file for a v2 project.

## Procedure

### Step 1: Establish ground truth, and check the version floor

Read `references/detect.md` and follow it. It lists the files to read, the commands to run, and the facts to record.

The single most common mistake is reading versions from `package.json`. Caret ranges are not installed versions. Get the resolved version from the lockfile.

The second most common mistake is reading them from the wrong lockfile. A repository can contain several installs that disagree, so decide which tree the plan is for, take every version fact from that tree, and name it in the report header.

If there is no Sanity Studio in this repository, say so and stop.

**Then check the floor before doing anything else.** If the resolved `sanity` major is below 3, or there is no `sanity` package and the project depends on v2-era packages such as `@sanity/base` or `@sanity/desk-tool`, or there is a `sanity.json` and no `sanity.config.*`, this is a v2 project. Stop there and tell the reader:

1. What you detected, and the evidence for it
2. That this skill covers v3 and later, and why v2 to v3 is a different kind of project: a rewrite of the configuration and plugin layer, not a version bump
3. That the right starting point is the official v2 to v3 migration guide, which they should find on the Sanity docs site rather than take from you second-hand
4. That every v2-era plugin needs its own compatibility check, since many were never ported
5. That once they are on v3, running this skill again will plan the rest of the span, which is a much smaller job

Be useful about it. A hard stop that explains itself and points somewhere is a good answer; a refusal is not. But do not soften it into a partial plan, and do not estimate the v2 to v3 work.

If both `sanity.json` and `sanity.config.*` are present, treat the resolved `sanity` version as authoritative. A leftover `sanity.json` in an otherwise v3+ project is worth mentioning as cleanup, not a reason to stop.

### Step 2: Determine the span

You need two versions: **from** (resolved, installed) and **to** (the target).

Read `references/version-lookup.md` for how to query current versions and changelogs authoritatively.

**The target is `latest`.** Recommend it and give the reasoning. Landing short means doing the whole job and still being behind, and more importantly, current plugin versions track current core, so an older core with current plugins often produces a worse dependency tree than the newest release does. Report the target's publish date and a one-line fallback so the reader can apply constraints you cannot see, but that is disclosure, not a hedge, and it does not change the recommendation. Deviate only for a constraint the reader has actually stated, and then give the pin a review date.

State the span explicitly in the report. A v3 project and a v5.31 project are completely different jobs and the reader needs to know which one they have.

### Step 3: Load only the boundaries you cross

Read `references/boundaries.md` first. It is a short index: the coverage bounds, the two-edge fetch rule, and a table saying which boundary files the span needs.

**Then read only those files.** Boundary content is split one file per major, so a span that crosses one boundary loads one file rather than all of them. A project on 5.31 crossing into v6 reads the index, `boundary.v6.md` and `deprecations.md`, and nothing else: the v4 and v5 files would cost context and invite findings that do not apply.

Each `boundary.v*.md` holds the crossing *into* that major plus the undeclared changes *inside* that major's line, because a span that crosses a boundary lands somewhere in the line above it and needs both.

Then read `references/package-coupling.md`. It applies to every span.

### Step 4: Fetch what the references do not cover

`references/boundaries.md` declares **two** bounds on its within-line coverage, and both need respecting.

Fetch the changelog for any part of the span that falls **above the upper bound** or **below the lower bound**. A project starting below the lower bound needs two fetches: from its current version up to that bound, and from the upper bound to the target. The curated middle is used as-is.

The lower bound matters as much as the upper one, and it fails more quietly. Without it you will trust the file for a range it never examined and report that nothing there applies, which reads as a verified finding and is actually an unexamined gap. **Never state that a range is clean unless it sits inside the declared bounds or you fetched it yourself and got real content back.**

That last clause is not padding. Below the lower bound the upstream changelog does not exist at all, so the fetch returns almost nothing, and **an empty fetch is not a clean range.** `boundaries.md` says which range this affects and what to report instead. Reporting silence as "nothing applies" there is the single easiest way for this skill to produce a confident lie.

Pay particular attention to breaking changes that shipped in **minor** releases without being labelled as breaking. Those are the ones that surprise people, because nobody reads minor release notes across forty releases. The references list known examples; assume more exist.

### Step 5: Test applicability

For each candidate change, find the condition in the repository. If you cannot determine it from code, it goes in the human questions section, not in the findings.

Examples of the difference:

- "Custom auth providers now replace built-ins instead of appending" → grep the config for an `auth` block. Present or absent. **Determinable.**
- "Default search strategy changed, results will shift" → whether that matters depends on how editors search. **Not determinable. Ask.**

Prefer conditions you can actually evaluate. `grep -r "data-slate"` is a real test. "Do you have complex custom components?" is not.

### Step 6: Resolve the dependency graph

Apply `references/package-coupling.md`. This is where most upgrades actually break, and it is the part a changelog cannot tell you, because it depends on which packages this project happens to depend on directly.

The rule that matters most: for any `@sanity/*` package the project depends on **directly**, check what version the *target* `sanity` release depends on and match that, rather than taking the package's own `latest`. Taking `latest` for a shared package like `@sanity/ui` puts two majors of it in one dependency tree, which produces duplicate-context errors and unstyled components that look like unrelated bugs.

### Step 6b: Assess plugins

Read `references/plugins.md` and follow it whenever the project has any plugin, which is nearly always.

Plugins block more upgrades than the Studio itself does, so they get their own pass and their own section in the report. Three things to carry into it: verify every plugin's compatibility rather than assuming it from who publishes it; let ownership decide the *path* once something is incompatible, not the verdict; and verify the *API shape* of any snippet you write against the package, separately from verifying its version, because a compatible version can still have renamed the export you are about to recommend.

### Step 7: Write the report

Follow `references/report-template.md` exactly. Write it to `SANITY-UPGRADE-PLAN.md` in the repository root unless the user names a different path.

Write a file rather than only printing to the terminal. The plan usually needs to reach people who are not at this keyboard: a lead, a reviewer, a support engineer. A file can go into a pull request or be pasted into a thread; terminal output cannot.

## Judgment calls worth making explicitly

**Recommend a sequence, not a single jump.** Crossing several majors in one commit makes failures impossible to attribute. Suggest intermediate stops at the last release of each major, and say why: it isolates each set of changes.

But a stop is only real if it is reachable. Each one needs its plugin versions resolved, because a plugin peering only recent majors can make an intermediate stop impossible to install. If you cannot resolve a stop, label it provisional rather than presenting the sequence as validated. Fewer verified stops beat more hypothetical ones.

**When you drop a stop, name what it costs.** Skipping one is often right: a boundary whose only applicable change is a Node floor is not worth a commit of its own, and a stop that cannot be reached without a forced duplicate major is worse than no stop. But the resulting hop then crosses two boundaries at once, and that is the exact thing the sequence exists to prevent. A plan that shows the evidence for skipping and stays silent on the consequence reads as though the trade were free.

So say both halves: why the stop was dropped, and that a failure in the combined hop could originate at either boundary, so debugging it means bisecting rather than reading the plan. One sentence covers it. The reader can accept the trade once they can see it.

**Size the work honestly.** If the project has two files importing `@sanity/ui` and no custom auth, say the upgrade is small. Inflating scope to look thorough wastes the reader's week. Equally, if the project imports from `sanity/_internal` or has a large custom component surface, say the upgrade is substantial rather than producing a plan that makes hard work look easy.

**Make the plan survive being skimmed.** It will be. A multi-boundary plan runs to several thousand words, and the person reading it is about to spend a week on the work, not an afternoon studying the document. So the top of the plan carries the whole thing in one screen, everything below it is reference to be read as the work reaches it, and findings are grouped by what the reader has to do rather than by which release the change came from. The specific failure to design against: a change that fails silently, sitting in a uniform table between two cosmetic ones, at identical visual weight. Length is not the enemy; flat weighting is.

**Write for the engineer who has to do the work, and describe the tree rather than the team.** This plan is usually read by someone at the customer, often someone who did not make the decisions that produced the current state. Keep the evidence, the specificity and the severity exactly as strong as the facts support: softening a real blocker or a real cost is a disservice, and an engineer can tell. What to drop is the implied verdict. "The deploy pipeline builds from the caret ranges rather than the lockfile" and "whoever set this up got it wrong" contain the same finding, and only one of them gets acted on. Reserve "you" for what to do next. Stay inside the upgrade, too: an unused dependency is not an upgrade finding, and a plan that drifts into general code review spends the reader's attention on things that do not gate the work.

**Name what you could not see.** Monorepos hide things: a `tsconfig.json` that extends a base file outside the repository, a workspace root that hoists React, a CI config in another directory. If a fact you needed was out of reach, put it in the human questions section by name. "I could not read `moduleResolution` because your tsconfig extends a file outside this repo" is useful. Silently omitting it is not.

**Do not promise roadmaps.** If asked whether a deprecated API will return or be removed, describe its current documented state and stop. Never speculate about future releases.

## Reference files

| File | Read when |
| --- | --- |
| `references/detect.md` | Always, at step 1 |
| `references/version-lookup.md` | Always, at step 2 and step 4 |
| `references/boundaries.md` | Always, at step 3. Index: coverage bounds and which boundary files to read |
| `references/boundary.v4.md` | Step 3, if the span crosses into or sits in v4 |
| `references/boundary.v5.md` | Step 3, if the span crosses into or sits in v5 |
| `references/boundary.v6.md` | Step 3, if the span crosses into or sits in v6 |
| `references/deprecations.md` | Step 3, always. Short, and not tied to a boundary |
| `references/package-coupling.md` | Always, at step 6 |
| `references/plugins.md` | Step 6b, whenever the project has plugins |
| `references/report-template.md` | Step 7 |

<!-- chapter:end slug=sanity-studio-upgrade -->

---

<!-- chapter:begin slug=seo-aeo-best-practices position=8 -->

## 8. seo-aeo-best-practices

- **Source:** https://github.com/sanity-io/agent-toolkit/blob/main/skills/seo-aeo-best-practices/SKILL.md
- **Raw:** https://raw.githubusercontent.com/sanity-io/agent-toolkit/main/skills/seo-aeo-best-practices/SKILL.md
- **Markdown:** https://skillsdocs.com/sanity-io/agent-toolkit/seo-aeo-best-practices.md
- **Licence:** MIT — https://spdx.org/licenses/MIT.html

Bundled files (4), referenced from this skill's directory:
  - `references/aeo-considerations.md` — https://raw.githubusercontent.com/sanity-io/agent-toolkit/main/skills/seo-aeo-best-practices/references/aeo-considerations.md
  - `references/eeat-principles.md` — https://raw.githubusercontent.com/sanity-io/agent-toolkit/main/skills/seo-aeo-best-practices/references/eeat-principles.md
  - `references/structured-data.md` — https://raw.githubusercontent.com/sanity-io/agent-toolkit/main/skills/seo-aeo-best-practices/references/structured-data.md
  - `references/technical-seo.md` — https://raw.githubusercontent.com/sanity-io/agent-toolkit/main/skills/seo-aeo-best-practices/references/technical-seo.md

<!-- Verbatim upstream SKILL.md follows, YAML frontmatter included. -->

---
name: seo-aeo-best-practices
description: SEO and AEO best practices for metadata, Open Graph, sitemaps, robots.txt, hreflang, JSON-LD structured data, EEAT, and content optimized for search engines and AI answer surfaces. Use this skill when implementing page SEO, technical SEO, schema markup, international SEO, AI-overview readiness, or improving content for Google, ChatGPT, Perplexity, and similar assistants.
---

# SEO & AEO Best Practices

Principles for optimizing content for both traditional search engines (SEO) and AI-powered answer engines (AEO). Includes Google's EEAT guidelines and structured data implementation.

## When to Apply

Reference these guidelines when:
- Implementing metadata and Open Graph tags
- Creating sitemaps and robots.txt
- Adding JSON-LD structured data
- Optimizing content for featured snippets
- Preparing content for AI assistants (ChatGPT, Perplexity, etc.)
- Evaluating content quality using EEAT principles

## Core Concepts

### SEO (Search Engine Optimization)
Optimizing content to rank well in traditional search results (Google, Bing).

### AEO (Answer Engine Optimization)
Optimizing content to be selected as authoritative answers by AI systems.

### EEAT (Experience, Expertise, Authoritativeness, Trustworthiness)
Google's framework for evaluating content quality.

## References

Start with the one reference that matches the task, such as technical SEO, structured data, EEAT, or AI-answer readiness. See `references/` for detailed guidance:
- `references/eeat-principles.md` — EEAT implementation and author schema
- `references/structured-data.md` — JSON-LD patterns (Article, FAQ, Breadcrumb, Product)
- `references/technical-seo.md` — Technical SEO checklist (metadata, sitemaps, hreflang, robots.txt)
- `references/aeo-considerations.md` — AI/AEO considerations (AI Overviews, crawler management)

<!-- chapter:end slug=seo-aeo-best-practices -->
