Thank you for your interest in contributing to the Sanity Plugins monorepo! This guide will help you get set up and explain our workflows.
For AI Agents: See AGENTS.md for agent-specific instructions and quick reference commands.
- Development Setup
- Running the Test Studio
- Code Quality
- Adding a New Plugin
- Plugin Ownership Model
- Publishing Packages
- Commit Guidelines
- Node.js (latest LTS)
- pnpm v10 or later — the exact version is managed via
packageManagerin rootpackage.json
# Enable corepack to automatically use the correct pnpm version
corepack enable
# Install all dependencies
pnpm installThe test studio located at dev/test-studio is a Sanity Studio instance that includes all plugins from this monorepo. It's the primary way to develop and test plugins locally.
# Start the test studio in development mode
pnpm devThis starts the Sanity Studio dev server at http://localhost:3333. The studio is pre-configured with all plugins and includes example schemas to test plugin functionality.
Note: The test studio requires Sanity user authentication in the browser. When you access the studio, you'll need to log in with your Sanity account. Simply accessing the URL isn't enough—the studio connects to Sanity APIs and requires authenticated access to the configured project.
The studio is deployed in two places:
- https://plugins.sanity.studio - deployed on merges to main
- https://plugins-studio.sanity.dev - also deployed on merges to main, and creates vercel preview deployments on PRs
- Add your plugin as a dependency in
dev/test-studio/package.json:
{
"dependencies": {
"your-plugin-name": "workspace:*"
}
}-
Import and configure your plugin in
dev/test-studio/sanity.config.ts -
If your plugin requires schema types, add them to the appropriate schema file in
dev/test-studio/src/
We use oxfmt for code formatting:
# Format all files
pnpm formatWe use oxlint for all linting (including React Compiler rules via the react-hooks-js plugin):
# Run the linter
pnpm lintType checking is performed by oxlint when running the lint command.
The monorepo uses Vitest v4 for testing.
# Run all tests (non-watch mode)
pnpm test run
# Run tests in watch mode (default vitest behavior)
pnpm test
# Update snapshots
pnpm test -uTests are co-located with source code in the src/ directory using .test.ts or .spec.ts extensions. Each plugin includes a package exports test to verify all exports are valid. Tests run from the root and require packages to be built first.
When creating a new plugin with pnpm generate, test files and configuration are automatically created. The root vitest.config.ts uses glob patterns to automatically discover all plugins, so no manual test configuration is needed when adding new plugins.
When tests need custom timeouts, use the options object syntax as the second argument:
// Correct
test('my test', {timeout: 30_000}, async () => {
// ...
})
// Wrong (deprecated)
test('my test', async () => {}, 30000)Before submitting a PR, make sure all checks pass:
pnpm format
pnpm lint
pnpm build
pnpm testAnd attach a changeset:
pnpm changeset addAll new official Studio plugins should be added to this monorepo.
The plugins monorepo is the canonical home for all Studio plugins, whether built by the Studio App Team or any other team at Sanity.
Being in this monorepo does not mean the Studio team owns the plugin. The monorepo is a collaborative space. The Studio team owns the repository, tooling, and cross-cutting maintenance work, while the creating team owns the plugin itself.
You build it, you own it — The team or individual who builds a plugin is responsible for bugs, new features, and user support. This must be explicit from day one.
- Official plugin home: New official Studio plugins should be created in this monorepo instead of standalone repositories
- You build it, you own it: The team that creates the plugin is responsible for plugin-specific bugs, features, issue triage, and community support
- Studio team role: The Studio team maintains monorepo infrastructure, CI/release automation, and compatibility updates needed for Studio majors
- Owner is required: Every new plugin must add a dedicated owner entry in
.github/CODEOWNERS(prefer a GitHub team, not an individual) before merge
| Responsibility | Studio team | Plugin owner team |
|---|---|---|
| Monorepo tooling and CI | ✅ | |
| Release automation and publishing workflow | ✅ | |
| Studio major compatibility updates | ✅ | |
| PR review for monorepo standards and consistency | ✅ | |
| PR review for plugin domain correctness | ✅ | |
| Plugin-specific bug fixes | ✅ | |
| Plugin feature development | ✅ | |
| Issue triage and response for the plugin | ✅ | |
| Community support for plugin behavior and usage | ✅ |
For any new plugin PR, make sure all of the following are included:
- Plugin scaffold/code in
plugins/... - A
.changeset/*.mdfile for release automation - A
CODEOWNERSrule that assigns the plugin path to the owning team - Clear ownership details in the plugin
README.md(owning team and support expectations) - An entry in the root
README.md## Current Pluginstable
Without an explicit owner, new plugins should not be merged.
Trusted publishing configuration depends on whether the package is brand new (never published to npm) or already exists on npm.
If you're creating a package that has never been published to npm before:
- Go to the repository's Actions tab on GitHub
- Find the "Setup a new npm package with Trusted Publishing" workflow
- Click "Run workflow"
- Enter the package name (e.g.,
@sanity/my-new-pluginorsanity-plugin-my-feature) - Click "Run workflow" — the workflow will create the package on npm
Then, configure trusted publishing by running locally (requires npm >= 11.10.0):
npm trust github <package-name> --file=release.yml --repository=sanity-io/pluginsThis sets up OIDC-based trusted publishing so the release workflow can publish packages without storing npm tokens.
For packages that are already published to npm, configure trusted publishing using the npm CLI (requires npm >= 11.10.0):
npm trust github <package-name> --file=release.yml --repository=sanity-io/pluginsAlternative: Configure via npm website
If you don't have npm >= 11.10.0, you can configure trusted publishing manually:
- Go to your package's access settings page:
https://www.npmjs.com/package/YOUR-PACKAGE-NAME/access - Under "Publishing access", click "Add a trusted publisher" and select "GitHub Actions"
- Fill in the fields exactly as shown:
| Setting | Value |
|---|---|
| Owner | sanity-io |
| Repository | plugins |
| Workflow | release.yml |
| Environment name | (leave empty) |
- Click "Add trusted publisher"
Run the generator and follow the prompts:
pnpm generate "new plugin"You can now iterate on the plugin with hot reloading in the test studio:
pnpm devBefore opening your PR, add the new package to the root README.md ## Current Plugins table so the monorepo plugin list stays current.
The trusted publishing workflow publishes an initial 0.0.1 placeholder version. To publish the real first version, create a changeset:
pnpm changeset addWhen prompted:
- Select your new package
- Choose major for the version bump (to release
1.0.0) - Enter
Initial releaseas the summary
Commit the changeset file with your PR.
Since the plugin is already published to npm, configure trusted publishing using the npm CLI (requires npm >= 11.10.0):
npm trust github <package-name> --file=release.yml --repository=sanity-io/pluginsSee For Existing Packages above for alternative manual instructions.
Follow the prompts in the generator:
pnpm generate "copy plugin"The generator will:
- Fetch metadata from the published npm package
- Copy over
version,description, andkeywords - Copy over
dependencies, automatically filtering out:@sanity/incompatible-plugin(test package)styled-components(should always be a peer dependency)sanity(should always be a peer/dev dependency)
- Detect if the plugin uses
styled-componentsbased ondevDependenciesandpeerDependencies
Note: You'll need to manually review and copy over any relevant peerDependencies and devDependencies as needed.
Refer to the generated README.md file in the plugin workspace for how to complete the last manual steps.
You can run pnpm dev to quickly see how the plugin works in the test studio as you migrate code.
When moving a plugin to this monorepo the conventions enforced on the repo typically warrant a new major version:
- enabling React Compiler
- Dropping CJS
- Requiring Sanity Studio v5 as the baseline
pnpm changeset addWhen prompted:
- Select your new package
- Choose major for the version bump
- Enter a summary of the changes that are breaking, and other changes that might affect runtime in order to pass linting and strict type checks.
Commit the changeset file with your PR.
After the plugin has been successfully migrated and published from the monorepo, update the original repository to inform users about the move:
-
Update the README in the original repository to include a notice at the top:
> **Note:** This repository has been moved to the [sanity-io/plugins](https://github.com/sanity-io/plugins) monorepo. > > All future development, issues, and releases will be managed there. > > - New location: `plugins/PLUGIN-NAME` (e.g., `plugins/@sanity/document-internationalization`) > - npm package: The package name remains the same > - Issues: Please open new issues in the [monorepo](https://github.com/sanity-io/plugins/issues)
-
Migrate existing issues to the monorepo:
- Review all open issues in the original repository
- Add a label matching the plugin name to each issue for organization (example: an issue that was once in the
@sanity/color-inputrepo will have a tagcolor-inputin this repo) - Move the issues to the monorepo's issue tracker
- Update issue references as needed
-
Archive the repository: Archive the original repository on GitHub to prevent new issues and PRs while keeping the history accessible.
See this example PR for reference.
This monorepo uses Changesets for version management and publishing.
You can publish preview versions of changed plugin packages directly from a PR using pkg.pr.new.
The preview workflow runs on PR events only when the PR has the trigger: preview label.
- Open your PR
- Add the
trigger: previewlabel - Wait for the
Publishworkflow (.github/workflows/pkg-pr-new.yml) to finish
The workflow detects changed packages from .changeset/*.md files in your PR diff and publishes only matching plugin packages.
- It ignores
.changeset/README.md - It supports package names that map to:
@sanity/*->plugins/@sanity/*sanity-plugin-*->plugins/sanity-plugin-*
If no valid changesets are found, no packages are published.
After the workflow runs, it posts (or updates) a PR comment titled "Preview this PR with pkg.pr.new" that includes install commands for each published package (for both pnpm and npm).
Use the provided command in another project to test the preview package version, for example:
pnpm install <pkg.pr.new url from the PR comment>When you make changes that should be released:
pnpm changeset addFollow the prompts to:
- Select the packages that have changed
- Choose the version bump type (patch/minor/major)
- Write a summary of the changes
This creates a changeset file in .changeset/ that should be committed with your PR.
- Merge PRs that include changeset files to
main - The release workflow automatically creates a "Version Packages" PR that bumps versions and updates changelogs
- When the "Version Packages" PR is merged, packages are automatically published to npm with provenance
- Write clear, descriptive commit messages
- Reference relevant issues when applicable
- Keep commits focused on a single change
If you have questions or need help, please:
- Open an issue in this repository
- Join the Sanity Community Discord