sanity-plugin-best-practices
Guides developers on code conventions and anti-patterns for Sanity Studio plugins in the monorepo, covering styling with vanilla-extract, component structure…
Identity proven by GitHub's verified organisation badge (sanity-io)
- Agents
- Claude Code, Codex, Cursor, Gemini CLI, GitHub Copilot, OpenCode, claude.ai and API
- License
- MIT (permissive)
- Pin
- a80a997pinned Oct 9, 2026
- Context cost
- about 1,600 tokenswhen the skill loads
- GitHub stars
- 20on sanity-io/plugins
- Scripts
- None found
What it does
“Anti-patterns and best practices for building Sanity Studio plugins in this monorepo. Use when writing, reviewing, or refactoring plugin code under plugins/ — especially for styling/CSS, component performance, and runtime cost. Triggers on vanilla-extract, raw <style> tags, styled-components, theming, inline styles, or questions about how plugins should be structured here.”
Generated summary from the skill's own text
Guides developers on code conventions and anti-patterns for Sanity Studio plugins in the monorepo, covering styling with vanilla-extract, component structure, and React 19 patterns. The skill ensures plugins maintain consistent styling, theme awareness, and runtime performance through concrete examples from existing plugins.
When to use it
- Writing or reviewing plugin components under plugins/ directory
- Adding or changing styling with vanilla-extract, inline styles, or CSS
- Migrating external plugins into the monorepo
- Investigating plugin runtime cost, re-renders, or theme integration issues
When not to
- Building applications outside the sanity-io/plugins monorepo
- General React or Next.js patterns not plugin-specific
- Styling projects not using vanilla-extract or @sanity/ui
Install
These commands install from the publisher's repository and need no account.
skills CLI
npx skills add sanity-io/plugins --skill sanity-plugin-best-practicesInstalls from the repository's default branch, which may have changed since the commit we checked.
GitHub CLI 2.90 or later
gh skill install sanity-io/plugins sanity-plugin-best-practices@a80a997e3d11d188f9639ac3efab1d8f93a276efInstalls the commit we checked (a80a997).
Copy by hand
Download the folder at a80a997 and put it in .claude/skills/sanity-plugin-best-practices for Claude Code and in .agents/skills/sanity-plugin-best-practices for Codex, Cursor, Gemini CLI, GitHub Copilot and OpenCode.
Claude Code plugin
/plugin install sanity@claude-plugins-officialThe publisher's plugin in Claude's official marketplace. It may hold more than this skill, and we have not pinned it.
Copy for your agent
A prompt for any coding agent. It installs this skill from GitHub at the pinned commit, copies the files as they are, and runs nothing unless you ask.
Prompt
Install the agent skill "sanity-plugin-best-practices" by Sanity. Source, pinned to commit a80a997e3d11d188f9639ac3efab1d8f93a276ef: https://github.com/sanity-io/plugins/tree/a80a997e3d11d188f9639ac3efab1d8f93a276ef/.agents/skills/sanity-plugin-best-practices 1. Download that folder at exactly this commit. Do not use the default branch. 2. Copy every file unchanged, keeping the folder structure, into .claude/skills/sanity-plugin-best-practices/ for Claude Code and .agents/skills/sanity-plugin-best-practices/ for Codex, Cursor, Gemini CLI, GitHub Copilot and OpenCode. Create the folders if they do not exist. 3. Do not edit the files, and do not run any script in the folder unless I ask you to. 4. When you are done, list the files you copied and where you put them. Listing and checks: https://www.agentnative.dev/skills/sanity/sanity-plugin-best-practices
SKILL.md at the pin
---
name: sanity-plugin-best-practices
description: Anti-patterns and best practices for building Sanity Studio plugins in this monorepo. Use when writing, reviewing, or refactoring plugin code under plugins/ — especially for styling/CSS, component performance, and runtime cost. Triggers on vanilla-extract, raw <style> tags, styled-components, theming, inline styles, or questions about how plugins should be structured here.
metadata:
author: Sanity.io
version: '1.2.0'
---
# Sanity Plugin Best Practices
Conventions for authoring plugins in the `sanity-io/plugins` monorepo, captured as concrete
anti-patterns and the preferred approach. These rules encode what the existing plugins already do
so new and migrated code stays consistent, fast, and theme-aware.
This complements the broader React performance guide in the
[`vercel-react-best-practices`](../vercel-react-best-practices/SKILL.md) skill — read that for
general React/Next.js patterns, and this skill for plugin-specific guidance.
## When to Use This Skill
Use this skill when you are:
- Writing or reviewing a plugin component under `plugins/` (`.ts`/`.tsx`) or a test-studio example.
- Adding or changing styling: authoring `.css.ts` (vanilla-extract), reaching for `<style>` tags,
inline `style={{}}`, CSS files, or `styled-components`.
- Migrating an external plugin into the monorepo (pair with the `plugin-transfer` skill).
- Investigating runtime cost, re-renders, or styling that does not respect the Studio theme.
- Deciding how a plugin should be structured to match the rest of the repo.
## How to Use
Read the reference file for the area you are working in. Each reference lists the anti-pattern, the
preferred approach, and `Incorrect` / `Correct` examples grounded in real plugins in this repo.
| Area | What it covers | Reference |
| --------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ | -------------------------------------------------- |
| Styling and CSS | Use vanilla-extract (static + dynamic) for all plugin styling; encapsulate classes in components; migrate `styled-components` to vanilla-extract | [`references/styling.md`](./references/styling.md) |
| Component refs | Don't use `forwardRef` — on React 19 `ref` is a regular prop (enforced by lint) | [`references/refs.md`](./references/refs.md) |
> This skill is intended to grow. When you find a plugin pattern worth standardizing (or an
> anti-pattern worth banning), add a focused reference file and a row to the table above rather than
> bloating this entry point.
## Styling: the one-line rule
[vanilla-extract](https://vanilla-extract.style) is the styling solution for everything you author —
in new plugins and existing ones. It compiles to a static stylesheet (zero per-render and
per-instance cost), is type-safe and scoped, and lives in a `.css.ts` next to the component:
1. **vanilla-extract `style()`** — static styles (the common case).
2. **vanilla-extract `createVar()` + `assignInlineVars()`** — dynamic styles that read the live
`@sanity/ui` theme (via the `useTheme_v2()` hook) or vary with props/state.
3. **Import a third-party `.css`** once at module scope — for prebuilt stylesheets you don't author
(katex, easymde, …).
4. **Never** render raw `<style>` tags (in JSX, via `dangerouslySetInnerHTML`, by appending a
`<style>`/`<link>` to `document.head`, or via `useInsertionEffect`). They re-parse on every
render, duplicate per instance, bypass the theme, and are an injection risk. CSS-in-JS is a last
resort: only when the CSS is genuinely dynamic (from runtime data, able to be _any_ CSS) reach for
`styled-components` — the most performant CSS-in-JS — and we try hard never to need it.
`styled-components` is **legacy** — the Studio's old styling library, still in some plugins. Migrate
existing styling usage to vanilla-extract (carefully, preserving visual fidelity; not during a
transfer's initial port). Its only remaining sanctioned use is the last-resort CSS-in-JS escape hatch
in rule 4 (arbitrary CSS from runtime data). See
[`references/styling.md`](./references/styling.md#migrating-off-styled-components) for the patterns
and the [`migrate-styled-components-to-vanilla-extract`](../migrate-styled-components-to-vanilla-extract/SKILL.md)
skill for the step-by-step migration procedure.
See [`references/styling.md`](./references/styling.md) for the full rationale and examples.
## General principles
- **Build on `@sanity/ui`.** Compose Studio primitives (`Box`, `Card`, `Flex`, `Stack`, `Text`,
`Button`, …) and read design tokens with the `useTheme_v2()` hook (`color...`, `space[...]`,
`radius[...]`, `font...`) instead of hardcoding colors, spacing, or fonts.
- **Style with the priority above:** vanilla-extract for all new styling — `style()` for static,
`createVar()` + `assignInlineVars()` (from `@vanilla-extract/dynamic`) for dynamic; never raw
`<style>` tags.
- **Encapsulate styles in components; don't scatter `className`.** Wrap each vanilla-extract class in
a small component that spreads props onto the `@sanity/ui` primitive, keeping the same export name
and API as the old `styled()` so call sites are unchanged — composition stays as clean as before.
To override a primitive's own styles, use the `selectors: {'&&': {...}}` specificity trick
(styled-components guaranteed override ordering in the CSSOM; vanilla-extract does not). See
[`references/styling.md`](./references/styling.md#keep-the-component-layer-encapsulation).
- **`styled-components` is legacy — migrate it to vanilla-extract.** Never add it for ordinary
styling (its only sanctioned use is the last-resort CSS-in-JS escape hatch for arbitrary runtime
CSS); convert existing styling usage to vanilla-extract (carefully, preserving visual fidelity)
using the patterns above. Until a plugin is fully migrated, keep its `styled-components` peer +
`catalog:` devDependency aligned so it resolves to the single instance shared with the Studio;
remove them once migrated, and lock it in with `no-restricted-imports`. See
[`references/styling.md`](./references/styling.md#migrating-off-styled-components).
- **Use `lodash-es`, never `lodash`** (matches `AGENTS.md`).
- **Never use `forwardRef`.** On React 19 `ref` is a regular prop — declare `ref?: Ref<T>` in your
props and use it directly. The lint config bans the `forwardRef` import. See
[`references/refs.md`](./references/refs.md).
- **Apply the React performance rules** from `vercel-react-best-practices` (don't define components
inside components, batch DOM/CSS writes, hoist static JSX, etc.).
From sanity-io/plugins at a80a997, shown as plain text under its license: MIT (permissive). The license text is in LICENSE.
View SKILL.md on GitHub at a80a997Files
- SKILL.md7.0 KB · 2f463467adbd
- references/
- refs.md4.1 KB · 72a9239ece91
- styling.md28 KB · d580518d84ee
3 files, 39 KB in total. Each file has a sha256 in the pin's manifest; the first 12 characters are shown.
Skill folder on GitHub at a80a997More from Sanity
sanity-live-cache-components
Integrates Sanity Live with Next.js Cache Components in next-sanity v13+ apps, setting up shared cached data fetching through sanityFetch, live content…
Claude Code, Codex, Cursor +4 · about 4K tokens · pinned 6 days ago
content-experimentation-best-practices
This skill provides guidance for designing and running content experiments using A/B and multivariate testing.
Claude Code, Codex, Cursor +4 · about 360 tokens · pinned 6 days ago
content-modeling-best-practices
Guides agents through structured content modeling principles for headless CMSes like Sanity, covering schema design, content reuse strategies, and field…
Claude Code, Codex, Cursor +4 · about 340 tokens · pinned 6 days ago
portable-text-conversion
Converts HTML and Markdown content into Portable Text blocks for Sanity using markdownToPortableText, htmlToBlocks, or manual construction.
Claude Code, Codex, Cursor +4 · about 510 tokens · pinned 6 days ago
portable-text-serialization
This skill enables rendering Portable Text content across multiple frameworks and output formats using the @portabletext library family.
Claude Code, Codex, Cursor +4 · about 940 tokens · pinned 6 days ago
sanity-best-practices
This skill provides best practices for Sanity headless CMS development across schema design, GROQ queries, framework integrations (Next.js, Nuxt, Astro…
Claude Code, Codex, Cursor +4 · about 1.1K tokens · pinned 6 days ago
Related in Frontend and design
next-bundle-optimizer
Audits Next.js applications to identify and reduce browser bundle size and initial-load work by analyzing routes, dependencies and client outputs.
by VercelOfficial publisher
Claude Code, Codex, Cursor +4 · about 2.3K tokens · pinned 6 days ago
next-cache-components-optimizer
Optimize Next.js routes to display meaningful UI instantly on initial load or client navigation by encoding the optimization goal as a failing…
by VercelOfficial publisher
Claude Code, Codex, Cursor +4 · about 5.6K tokens · pinned 6 days ago
next-partial-prefetching-adoption
Enables Partial Prefetching in Next.js 16.3+ apps and guides adoption through auditing Link prefetch behavior, preserving UI in tests or manually, and…
by VercelOfficial publisher
Claude Code, Codex, Cursor +4 · about 6.7K tokens · pinned 6 days ago
remotion-interactivity
This skill teaches how to structure Remotion code so the Studio IDE recognizes and makes it interactive.
by RemotionOfficial publisher
Claude Code, Codex, Cursor +3 · about 2.4K tokens · pinned 6 days ago
remotion-interactivity
This skill teaches how to structure Remotion video components to unlock Studio interactivity features like click selection, drag-and-drop, resizing, rotation…
by RemotionOfficial publisher
Claude Code, Codex, Cursor +3 · about 4.8K tokens · pinned 6 days ago
remotion-maps
This skill enables creating map-based animations with Remotion using four different mapping libraries: Mapbox, MapLibre, MapTiler, or CesiumJS.
by RemotionOfficial publisher
Contains scripts
Claude Code, Codex, Cursor +3 · about 240 tokens · pinned 6 days ago