Studio workflow improvements, add metadata to releases, and bug fixes

Published: October 28, 2026

v6.21.0

This release adds custom metadata fields to content releases when using variants beta, moves version management to a document inspector for variants users, improves breadcrumb navigation and field interactions, fixes search functionality issues, and enhances overall stability with better loading states and error handling.

Download the schema from the diagnostics dialog

The Studio diagnostics dialog can now download the workspace schema to share with Sanity support.

Add configurable metadata fields to release forms

For Content Variants beta users, Studios can add extra fields to content releases with releases.metadata.fields when beta.variants is enabled, coming soon to the rest of studios. The definitions are the same defineField calls used in Structure. They appear on the create-release form and in the edit-release dialog. They do not appear in the inline editor, which is the release detail surface when variants are off. Empty config changes nothing.

beta: {
  variants: {enabled: true}
},
releases: {
  metadata: {
    fields: [
      defineField({
        name: 'legalReview',
        title: 'Legal review',
        type: 'string',
        initialValue: 'notReviewed',
        options: {
          list: [
            {title: 'Not reviewed', value: 'notReviewed'},
            {title: 'Reviewed', value: 'reviewed'},
          ],
        },
      }),
    ],
  },
}

Allowed resolved types are string (including an options list), text, number, boolean, date, datetime, url, and object whose own fields are allowed. reference, crossDatasetReference, image, file, Portable Text, array, and anything else are dropped with a development warning. Field validation rules run, and create or save is blocked while a field has an error or while its check is still running. initialValue is applied once on create. Clearing a field removes the key.

Field-level component overrides (components.input, components.field, and the other components keys) are intentionally unsupported for now. They can be added later without changing releases.metadata.fields.

These fields are not shown on the release detail page, on scheduled drafts, or in the releases table.

Move version management to document inspector

For Content Variants beta users, version management now lives in a document inspector. Open it with the "Manage versions" action in the document panel header. The inspector gives the interface more room and lets editors share a link directly to the version list.

🐛 Notable bugfixes and improvements

  • Breadcrumbs in nested object and array item dialogs now do what their label suggests. Clicking a field name scrolls to that field and focuses it, and clicking an item (a dialog title) scrolls that dialog back to the top. Clicking the title of the dialog that is already open no longer closes it.
  • Collapsible fieldsets and collapsible object fields can now be expanded or collapsed by clicking anywhere in their header row, not just the title.
  • Vision no longer shows its Variant option, nor sends a variant with queries, in workspaces where variants are switched off (beta.variants.enabled not set), matching the rest of the studio, which shows no variant selector there either.
  • Arrays that use the grid layout no longer force nested arrays to render as grid tiles. A nested array keeps its own item component and custom input, including inside an image dialog opened from a grid.
  • Every field and array item gets its own loading boundary again. If a custom form component (components.input, components.field, components.item, or a plugin's form.components) suspends, for example on an App SDK hook such as useDocumentProjection for a document that has not been created yet, only that field or array item shows a loading skeleton, instead of the whole document form staying on "Loading" (a regression in 6.17.0). Studio also logs a console warning naming the component, field path and document type, explaining how to add a Suspense boundary whose fallback reserves the component's space, so the form does not shift when it loads.
  • Studio search no longer fails with score() function received unexpected expression when search.unstable_partialIndexing.enabled is on and the dataset's partial index excludes a field that search would boost, such as a preview title, or when a slug field sits at the dataset's maximum field depth. Those fields are no longer boosted, and matching documents are still returned.
  • In a release's document list, Unpublish and Discard version now name your roles when you do not have permission to use them, matching the document footer.
  • Fixes Content Agent proposed changes not loading in a Studio inside Workbench when the page origin isn't a CORS origin with credentials on the project.
  • Fixes a crash in the Structure tool after reloading or switching perspectives while an array item whose _key contains a period or a closing bracket was open.
  • Studio search no longer lists Content Agent document versions when querying across all versions. Those versions were often duplicates of the real document and opened as an empty document for anyone who was not the author.
  • The Reload button on the structure tool's error screen now reloads only the structure tool instead of the whole page. It retries the structure; picking up a newly deployed Studio still needs a page reload.
  • Review changes now shows the same calendar day that is stored on a date field. Previously, in timezones behind UTC, the diff could show the previous day.
  • sanity schema extract --watch now re-extracts when you edit your schema or sanity.config file. Previously, it ran the first extraction and then never ran again, without reporting an error.
  • sanity typegen generate --watch now works with typegen.resources (beta). Previously, the command exited with "Watch mode does not support typegen.resources yet". A change to any resource's query files or local schema file regenerates every resource. A resource without a local schema uses the schema deployed to its dataset, which is fetched once when the watcher starts, so restart the watcher to pick up a newly deployed schema.
  • New sanity codemod dashboard command moves an existing Studio or SDK app onto the Dashboard. It rewrites sanity.cli.ts to wrap app in defineApplication, looks up the organization from the project, and prompts for a title and slug. The existing deployment stays live. The codemod warns when deployment.appId would make the first Dashboard deploy overwrite the existing app, and when the installed sanity version is too old for defineApplication.
  • sanity codemod no longer ends every run with an ExitError stack trace, even successful ones. It now waits for the codemod to finish and exits with its exit code, so a failed run is reported as a failure.
  • sanity tokens create can now create organization tokens, the same kind of token the Add API token dialog in Manage creates. One token can hold organization permissions (such as Context, Blueprints, and Media Library) and roles on several projects. Interactive runs show a single checklist of everything the token can access, then print the equivalent flags for use in CI, for example sanity tokens create ci --grant context=developer --grant project:<projectId>=editor. Existing --project-id and --role usage still creates a project token. Unattended runs with no grant and no project now fail with a usage error (exit code 2) instead of exit code 1.
  • sanity tokens list and sanity tokens delete accept --organization <id>, so you can find and revoke organization tokens from the CLI.
  • New sanity whoami command prints the user or API token the CLI is authenticated as: name, email, ID, login provider, and where the token came from (the CLI config file, the SANITY_AUTH_TOKEN environment variable, or the CLI execution context). SAML SSO logins also show the organization. Pass --json for machine-readable output. The command exits with code 1 when you aren't logged in.
  • Commands that support --json now return argument and runtime errors as JSON, and exit with a nonzero code when they fail. Successful responses keep their existing shape, and -j aliases still work. In help output, --json now appears with the global flags.
  • sanity deploy now refuses to deploy a Studio that is referenced by a Blueprint, and tells you to deploy it with sanity blueprints deploy instead.
  • The Dashboard links that sanity deploy and sanity undeploy print, including in dry runs and --json output, now use the app or Studio's configured slug instead of its application ID. Deployments without a slug still link by ID.
  • For Dashboard apps without an app view (panel-only apps), sanity deploy and its dry run now link to your organization's Dashboard. Previously, they printed an /applications/<slug> URL that returned a 404.
  • Background services in Dashboard apps can now respond to event topics. bus.subscribe(topic, (message) => message.reply(value)) works in a worker as it does on the page, so a worker can answer requests from its app's views.
  • sanity deploy now uses the federation manifest from the build it just ran, and reads it from the output directory only when the build is skipped. A federated build that doesn't write a federation manifest now fails with an error naming the missing file.
Sanity logosanity.ioAll Systems Operational© Sanity 2026
Change site theme