Introducing GROQ-powered Webhooks



Studio v2.21.7

  • Fixes a bug where removing an annotation from text would interfere with other annotations within that same block
  • Fixes a bug where the correct version of a document did not appear when selected in the select revision menu
  • CLI now supports an increased maximum dataset name length of up to 64 characters

Version released on: October 26, 2021

Fixes match operator bug and corrects Content-Type for /history and /export

Content Lake v2021-10-21

Fixes match operator bug

Up until now, the match operator has not handled wildcards correctly in some specific situations.

For example, name match "foo.*bar" would be equivalent to foo * bar, because . would be considered a word-splitting character, and so foo.*bar would never match the string

While rare, this also turned out to affect some languages where we would split words incorrectly: "創傷ケア" match "創傷ケア*" would return false, but "創傷ケア" match "創傷ケア" would return true.

We are now releasing the API version 20211021, which fixes this.

In short, the new algorithm honours Unicode word segmentation rules and considers the wildcard character, *, to be a word character. This fixes the internal tokenization to be consistent.

Correct Content-Type for /history and /export

Previously the /export and /history endpoints was returning Content-Type: text/plain even though they were streaming JSON. After this version they return Content-Type: application/x-ndjson instead.

Articles affected by this release

Version released on: October 21, 2021


Studio v2.21.6

Fixes an issue that could cause an error saying “This document is of type X and cannot be edited as Y.” when navigating between singleton documents

Version released on: October 21, 2021


Studio v2.21.5

Fixes a bug where if a space was configured as the default but not the first in the spaces array, the initial studio load would redirect to the correct URL but not configure the client to use the correct dataset.

  • Fixes an edge case for user-defined components. All user-defined components are re-rendered on navigation regardless if the structure child is a function or not.
  • Fixes an issue where user-defined components would not be hidden when pane was collapsed.
  • Fixes sluggish UI in the image select dialog when the list contained large GIFs.
  • Fixes image select dialog scrolling to top when loading a new page.
  • Fixes an issue where deploying a GraphQL API might crash if schema-imported component tries to access localStorage.
  • Fixes an issue where switching betweeen two "singletons" (document list items) without a defined schema type might result in the incorrect schema type being used.
  • Fixes a styling issue in the @sanity/vision autocomplete styling.
  • Fixes an earlier regression that caused the withDocument higher-order component to pass on the edited document before it was fully loaded.
  • Fixes a bug where removing an annotation in the beginning of a Portable Text block would accidentally mutate trailing annotations inside that same block.

Version released on: October 19, 2021


Studio v2.21.4

  • Fixes a bug where the withDocument higher-order component would sometimes return null initially
  • Fixes a bug where collapsed panes might not show their content when expanded
  • Fixes a bug where tools would not get passed the tool property, resulting in incorrect context

Version released on: October 15, 2021

Patch release - Several bugfixes

Studio v2.21.3

  • Fixes an issue that caused the GraphQL schema deployment to either be slow or hang on schemas with many references
  • Fixes an error where the image and file inputs might throw an error about unsetting deep values on a primitive value
  • Fixes glitches in logo animation when the studio is loading
  • Fixes an issue where the desk tool would in certain cases give an error message saying Unknown document type *
  • Fixes an issue where document list items inside of non-document lists would be rendered with “Untitled” as the title, instead of the full document preview
  • Fixes an issue where the schema error summary would not be displayed because the studio would crash before being able to display it
  • Fixes a regression that caused the withDocument higher-order component to get stuck on an initial null value
  • Fixes an error where the client would fail to set up a listener when sending huge queries, without giving a developer-friendly error message

Version released on: October 14, 2021


Studio v2.21.2

  • Fixes UX issues with collapsing and expanding panes in the Desk Tool.
  • Adds support for scrolling in the date picker‘s popover, when it’s not able to show all the contents at once.

Version released on: October 08, 2021


Studio v2.21.1

  • Fixes issue with experimental spaces not loading correctly (#2835)
  • Fixes an issue with nested document lists. (#2834)
  • Fixes an issue that would disallow deleting an unpublished document for users with access to update the draft
  • Fixes an issue that would disable discarding drafts even for users that had access to it

Version released on: October 07, 2021

Default layout and Desk tool migrated to Sanity UI

Studio v2.21.0

Fully migrated @sanity/default-layout to Sanity UI

The navbar and all of its elements have been migrated with @sanity/ui. The migration fixes a long-standing issue with responsiveness of the navbar, especially when it comes to the tool menu.

Fully migrated @sanity/desk-tool to Sanity UI

The pane system has been rewritten from scratch, and the UI of Desk Tool is completely refactored to using @sanity/ui.

Panes now support dynamic minimum and maximum widths, to provide a more responsive behavior.

Other features:

  • Newly initialized studios will now include a .gitignore
  • Speed of dataset imports improved when assets already exists
  • sanity hook commands updated to be forwards-compatible with upcoming webhook changes

Notable bugfixes:

  • fix(language-filter): fixes popover placement issue when language list is long (#2801)

Version released on: October 06, 2021

New GROQ-Powered Webhooks

Webhooks v2021-10-04

A complete revamp of the webhooks API, which allows for sophisticated webhook configuration using GROQ. Read more in the docs, or read the announcement blog post.

Migrating the legacy webhook behavior to GROQ-powered Webhooks

If you need to recreate the previous webhook behavior – triggering on all changes, and on a dataset level rather than document-level – you can do so by following these steps:

  1. Create a webhook set to trigger on create, update and delete
  2. Leave the Filter field empty
  3. Add the following to the Projection field
// webhook projection
  "transactionId": "Not supported",
  "projectId": sanity::projectId(),
  "dataset": sanity::dataset(),
  "ids": {
    "created": [
    	select(before() == null && after() != null => _id)
    "deleted": [
      select(before() != null && after() == null => _id)
    "updated": [
      select(before() != null && after() != null => _id)
    "all": [

You can also click this link to get a template with the settings described above.

Other improvements

In addition to the filter and projection functionality, we’ve made a bunch of other improvements as well:

  • There’s a new UI for webhooks in the management interface and a full RESTful API
  • Webhooks can now be edited after you have created them
  • You can enable and disable webhooks
  • Webhook entries contain more metadata, including name, description, and more
  • You can add custom headers, and specify its HTTP method
  • There’s a new attempts log that makes troubleshooting and testing easier
  • Webhooks support a secret that will be hashed in the request which you can use to authenticate that the request originates from your project
  • We include idempotency keys in the headers, which you can use when syncing data to verify you haven’t received it before
  • Webhook configurations can be shared via URL
  • Webhooks are now guaranteed to include the content changes that caused them
  • Webhooks will now be retried (and are also rate-limited to 60 per second)
  • We also added a `sanity` namespace to GROQ that can be used in projections

Articles affected by this release

Version released on: October 04, 2021

GraphQL Date comparison bug fix

GraphQL v2021-09-28

Fixes a bug when trying to filter on a Date type

Version released on: September 28, 2021

styled-component as first-level dependency

Studio v2.19.0

styled-component as a first-level dependency

We've been hard at work migrating the internals of Sanity Studio from CSS modules Sanity UI, which is based on styled-components.

Over the past releases, we've seen a few reported issues where multiple versions of the styled-components dependency could end up in the same studio, causing issues that were hard to understand (sanity is undefined) and would require a full dependency tree reinstall to resolve (rm -rf node_modules && sanity install).

In this version, we're introducing styled-components as a first-level dependency in the studio, in the same way that react and react-dom is.

The first time you run sanity start, sanity build or sanity upgrade after the upgrade, you will be asked to install the dependency - and in non-interactive it will install automatically.

This migration should be seamless for most studios. We anticipate that in certain cases (custom monorepo setups, for instance) it might require manual changes, like adding styled-components as a dependency of the studio.

For a more in-depth look at the rationale behind this change, see the pull request for this change.

Specify API version for CLI query command

The sanity documents query CLI command now has a new flag: --api-version.

This is useful for testing how queries behave in different API versions. For now, the command will still use v1 as the default.

Notable bugfixes

  • Fixes a rare case where an error could be thrown during the React render lifecycle without being an Error instance, which would crash the error boundary

Version released on: September 13, 2021

New login screens

Manage v2021-09-13

Login screens are refactored to use Sanity UI.

Version released on: September 13, 2021

Throw error on invalid image source

Image URL library v1.0.0

Previously, the @sanity/image-url library would return `null` if you passed an invalid source (such as an object without the expected properties, or an invalid image asset ID). From 1.0.0 and onwards, the library will throw when receiving an invalid source. This makes it more obvious when there is invalid data passed to it, as well as ensuring that the function always returns a string (which is a much requested feature for TypeScript users).

Version released on: September 01, 2021

Removing warning for new fields in GraphQL schemas

Studio v2.17.2

Removing warning for new fields in GraphQL schemas

If you add schema fields and run sanity graphql deploy you will no longer be warned about “dangerous changes.” Previously, our GraphQL validation followed the behavior of graphql.js, which treats added fields as “dangerous.” We have had a lot of feedback, that in practice, it has been more bothersome than useful.

🐛 Notable bugfixes

  • Since v2.16.0, (inline) references in Portable Text would not be saved properly and left an arbitrary attribute. This version fixes that.

Articles affected by this release

Version released on: August 31, 2021

Conditional Fields

Studio v2.17.0

A lot of you have been waiting for this for a while: A way to show and hide fields based on the value of some other field. The wait is over! In this release of the Sanity Studio the hidden field has been upgraded to take a callback function.

export default {
 type: 'object',
 name: 'dropdownExample',
 fields: [
     name: 'kind',
     type: 'string',
     options: {list: ['foo', 'bar', 'baz']},
     name: 'fooOptions',
     type: 'object',
     hidden: ({parent}) => !(parent?.kind === 'foo'),
     fields: [/* ... */],
     name: 'barOptions',
     type: 'number',
     hidden: ({parent}) => !(parent?.kind === 'bar'),
     fields: [/* ... */],
Conditional Fields with dropdown

Get more code examples and read more about the details in our docs.

Version released on: August 25, 2021


Studio v2.15.0


  • Migrates `color-input` to `@sanity/ui`
  • Migrates `google-maps-input` to `@sanity/ui`
  • Migrates the document status bar and document actions to `@sanity/ui`
  • Migrates the default preview to `@sanity/ui`


  • Adds a visible indicator on the focused day when using arrow keys to navigate through the calendar days in the date input
  • Fixes an issue that in some rare cases could make the preview selection result in an invalid query
  • Fixes an active state issue with `PaneItem` in `DocumentsListPane`

Version released on: August 17, 2021


Studio v2.14.0


  • Adds a notice for insufficient permissions to references
  • Adds strict dependency checking to all packages
  • Adds better feedback about accepted files when dragging over file and image inputs


  • Enforces that DateInput value is constrained to timeStep
  • Fixes issue where importing JSON files would crash the studio build process
  • Fixes an issue where the form was visible above the PTE toolbar in fullscreen mode
  • Adds safeguard against missing .focus() method on components used with the withDocument() higher order component.
  • Adds a visible indicator on the focused day when using arrow keys to navigate through the calendar days in the date input
  • Fixes an issue that in some rare cases could make the preview selection result in an invalid query

Version released on: August 11, 2021

Replace picosanity with full client

next-sanity v0.2.0

The first version of next-sanity shipped with the picosanity client built-in. This caused some confusion for people who wants not only to pull data from their content lake, but also send patches and mutations via API routes. Since picosanity only supported fetching content, it had a smaller bundle size than the full SDK.

This version replaces picosanity with the full client. You can use Next.js' tree shaking to keep the client out of the app bundle or install picosanity if you still prefer to use that.

Version released on: May 13, 2021


Studio v2.10.0

Initial values for any type

This upgrade extends support for initial values to any field, not just the document type. You can now set initial values on a field that's shared between schema types. It also allows for initial values for fields inside of arrays and the Portable Text editor, which has been a frequent request. Initial values still supports promises if you need to set values from external data sources.

Initial values for any type

🐛 Notable bugfixes

  • Fixes a few performance regressions in the array input
  • Fixes an issue where the "revert changes"-dialog confirmation would sometimes be hidden behind the form dialog

Articles affected by this release

Version released on: April 28, 2021


Studio v2.9.0

Improved image fields

The image fields has seen a number of bugfixes and now properly display presence and change indicators, including for hotspot/crop fields and other image fields that opens in dialogs.

Sanity UI powered reference input

The default reference input in Sanity Studio has been refactored to use the Autocomplete component from Sanity UI which improves overall accessibility and fixes keyboard navigation bugs.

Other features

  • The language filter can now be configured with an array of document types to control which schemas the filter will appear on (thanks @spacedawwg!).

🐛 Notable bugfixes

  • Fixes false positive with image fields in dialogs, indicating that there’s a change when there is none
  • The code input now wraps text inside the input so that all the code is visible without having to scroll horizontally (thanks @katherinepeterson!)

Version released on: April 26, 2021

Fix gatsbyImageData

Gatsby source plugin v7.0.3


  • Bring back the gatsbyImageData on image nodes
  • Set more specific peer dependencies and engines update

Explore the diff

Version released on: April 13, 2021

@sanity/ui v0.33.11

Sanity UI v0.33.11

Most changes in this version are improvements and bug fixes behind the scenes – however there are a few new APIs:

New properties

  • Introduced the onItemSelect property ((index: number) => void) to the Menu component props, to make it possible to subscribe to which MenuButton is currently selected.
  • The MenuItem now takes the selected property (boolean) to indicate which item in a Menu should be selected by default.
  • The MenuButton component now takes the popover property (PopoverProps), which is an object containing properties for the Popover component (like arrow, preventOverflow, portal, and so on).
  • The Popover component now takes a matchReferenceWidth property (boolean) to render the popover with same width as the referred element.

Version released on: March 29, 2021

Add support for API versioning

next-sanity v0.11.1

This release upgrades the picosanity dependency that brings support for configuring the client's API version:

import {
} from 'next-sanity'

const config = {
  dataset: process.env.NEXT_PUBLIC_SANITY_DATASET || 'production',
  projectId: process.env.NEXT_PUBLIC_SANITY_PROJECT_ID,
  useCdn: process.env.NODE_ENV === 'production',
apiVersion: '2021-03-25'
} export const sanityClient = createClient(config)

Version released on: March 26, 2021

Query optimizations and bug fixes

GROQ v2021-03-25

New features:

  • Optimize *[a[].b match "foo"]
  • Optimize nested paths (e.g. *[foo[].bar[].baz match "bar"]). This also makes them possible to use inside score().

Bug fixes:

  • Fix *[foo == null] when foo refers to an empty object
  • Fix some edge case bugs related to negation.

Version released on: March 25, 2021

GROQ specification compatibility, introduction of namespaces, and new functions

GROQ v2021-03-25

Content Lake is a full refactor of Sanity's backend and query engine. You can start using it by upgrading and specifying an API version for your client. Content Lake follows the GROQ specification and introduces both bug fixes and new features.

We have updated the documentation to reflect the newest version. This changelog contains the breaking changes and migration paths you need to take. You can take your time and stay on v1 for as long as you need, while you test and compare your queries against the new version. You can also specify the API version for only new queries to gradually move over. Let us know in the community if you have questions or need help.

Breaking changes from v1 to v2021-03-25

Correct parent operator behavior (^)

The GROQ ^ operator now works correctly in all scopes.

This fixes the known issue where the ^ operator only worked in subqueries. In all other scopes, it returned the root of the current scope, instead of the parent scope.

    // Old Behavior: "parentName" returns
    // New Behavior: "parentName" returns root name value
    "parentName": ^.name

Consistent handling of true/false/null for equality/comparisons operators

GROQ now uses three-valued logic consistently:

  • >, >=, <, <= returns null when the operands are of different types.
  • &&, || handles null "as expected": null && truenull and null || truetrue.

Note that == has changed slightly:

  • It now always return either true or false (never null).
  • You can compare against null and get the expected result: 123 == nullfalse and null == nulltrue.
  • Comparisons between other types than strings/booleans/numbers always return false.

This also means that in works with null: null in foo will return true if foo is [null, 1, 2].

Consistent array traversal

We have cleaned up the behavior for array traversal. For example, queries that contained multiple array traversals that don't work in v1, now work as expected in v2021-03-25 and onward.

*["link" in body[].markDefs[]._type]
// v1: No results
// v2021-03-25: An array of documents that has a link annotation in their "body" field

// Data:
    "_type": "book",
    "authors": [
      { "names": ["MH", "Holm"] },
      { "names": ["Bob"] },

// Query:
*[_type == "book"].authors[].names

// v1:

// v2021-03-25:

// You can also add an additional `[]` to flatten it completely:

// Query:
*[_type == "book"].authors[].names[]

// v2021-03-25:

Null values are not removed in projections

If you have stored null values in your documents, these are no longer removed in projections.

Override attributes while spreading objects and arrays (...)

You can now override attributes while using the spread operator (...).

// Data
[{"title": "A", "customTitle": "B", "_type": "post"}]

// Query:
*[_type == "post"]{..., "title": customTitle}

// Output from v1: 
[{"title": "A"}]

// Output from v2021-03-25:
[{"title": "B"}]

All numbers are float64

All numbers are now 64-bit floats. Previously we used 64-bit integer representation in certain contexts.

String ordering is more consistent

Previously ordering by strings would in some context use a "smart" numeric ordering instead of proper string comparison:

// Query:
["foo4", "foo12"]|order(@)

// v1:
["foo4", "foo12"]

// v2021-03-25:
["foo12", "foo4"]

// However, *|order(foo) has always (both before and after) used proper string comparison.

Deprecated: The is prefix operator

The is operator has been deprecated. You can use the equivalent comparison instead:

// v1:
*[_type is "post"]

// v2021-03-25:
*[_type == "post"]

Deprecated: Function calls without parentheses

Previously you could call functions without parentheses: *[length "foo" > 3]. This is now no longer possible and is a syntax error. You should use *[length(foo) > 3] instead.

Deprecated: $now and $identity

You can use now() and identity() instead.

Projections only work on objects, and returns null otherwise

Whenever you apply the projection operator ({}) on a non-object then it returns null instead of executing the object expression.

This means that the following query *[foo == bar][0]{a} now correctly returns null if there were no results. Previously this returned {}.

Negative indexes are respected in slicing

Previously negative indexes were not respected: [_type == "article"][-1] was equivalent to [_type == "article"][1]. This has now been fixed and -1 returns the last document.

Creating an empty attribute is now an error

The following query now fails: *[_type == "bar" && @[""] == "bar"] since empty attribute keys are not allowed.

order() only works on arrays, and returns null otherwise

Passing | order(…) on non-arrays return a null value instead of converting it to an array.


defined(x) is now only false when x is null. Previously it was false for empty arrays and objects as well.

New features for GROQ


Namespaces allow for a stronger grouping of functionality within the GROQ specification. They create dedicated spaces for global functions, as well as safer distinctions for specific implementations of GROQ. Learn more.

New functions: score() and boost()

The score() function computes a _score for each document from how well the expression matches the document. The documents will also be sorted by score (from high to low) if no other order() function is specified. Note that all documents will be scored (even those that don't match it at all) so you typically want to add a limit. Read more about scoring and boosting.

Portable Text to plain-text: pt::text()

The pt::text() function takes either a Portable Text block or an array of blocks and returns a string in which blocks are appended with a double newline character (\n\n). Text spans within a block are appended without space or newline. Read more about getting plain text from Portable Text.

New functions for geospatial queries

The geo namespace contains a number of useful functions for creating and querying against locations in your data. Query for distance, intersections, and more.

Performance improvements

Ordering and filtering on date times

It's now possible to filter and order efficiently on date times:

// Filter
*[_type == "post" && dateTime(publishedAt) > dateTime("2021-01-01T12:00:00")]

// Order
*[_type == "post"]|order(dateTime(publishedAt) desc)

Projections on huge documents

If you have huge documents, but your queries have projections that filter away most of the data (e.g., *[_type == "post"]{title, description}) you may see performance improvements.

Expressions with mixed && and ||

Expressions that use both && and || will now often be much faster (depending on how complicated they are).

  _type == "post" &&
  slug == "hello" &&
  ("news" in tags || products[0].name == "Sanity")

// v1: Slow!
// v2021-03-25: Much faster!

Version released on: March 25, 2021

Bug fixes

Content Lake v2021-03-25

The following bugfixes has been put into production:

  • Make listeners work in v2021-03-25
  • Fix bug where it was possible to change the _id of a document.
  • Fix bug which prevented datasets to be deleted

Version released on: March 25, 2021

Bug fixes in GROQ implementation

GROQ v2021-03-25

This release fixes smaller bugs for GROQ v2021-03-25.

  • Fixes a bug where dereferencing inside sliced arrays (e.g. *[_type == "book"]{"authors": authors[0...2]{era->}}) would return incorrect result.
  • Fixes a bug where scoping was incorrect while dereferencing arrays: *[_type == "book"]{"authors": authors[]->{..., "bookName": ^.name}}
  • select now produces a query error when given invalid arguments. Examples of invalid arguments are select(title, title2) (multiple fallback values) and select(title, isGood => title2) (alternatives after the fallback)

Articles affected by this release

Version released on: March 25, 2021

Revamped Date and DateTime inputs

Studio v2.6.3

This release includes a ground-up rewrite of the default Date and DateTime inputs in the Sanity Studio. The new date inputs are powered by Sanity UI and come with improved keyboard accessibility and fewer bugs.


Articles affected by this release

Version released on: March 17, 2021

Gatsby Source Plugin: v3 compability, Gatsby Image support, and bugfixes for preview

Gatsby source plugin v7.0.0

Breaking changes in v7.x

Starting at v7.0.0, this plugin requires Gatsby v3 and no longer supports Gatsby v2 or earlier. See the migration guide for instructions on how to upgrade to v7 of this plugin. If you're not yet ready to upgrade to Gatsby v3 we recommend staying at v6.x of this plugin.

Version released on: March 17, 2021

GraphQL generation 3 schema

GraphQL v2020-12-07

Breaking changes between gen2 and gen3

The third generation of the GraphQL API adds support for querying all documents through the new allDocument type. However, with a new generation comes breaking changes.

We had to rename a type to avoid conflicts. DocumentFilter has been renamed to Sanity_DocumentFilter. In other words, if you for example have a query like this:

query GetAllUser($docFilter: Sanity_DocumentFilter) {
  allUser(_: $docFilter) {

It should now look like this in gen3:

query GetAllUser($docFilter: DocumentFilter) {
  allUser(_: $docFilter) {

Articles affected by this release

Version released on: December 07, 2020

GraphQL Generation 2 schema

GraphQL v2020-02-27

Deploying generation 1

In order to use the old beta version of the GraphQL API, you can specify it when running the GraphQL command from the CLI. We call these older versions generations. The beta generation is named gen1, and the current generation is named gen2.

To deploy the old generation (beta), you can use the --generation flag in the CLI like this:

sanity graphql deploy --generation gen1

Breaking changes between gen1 and gen2

The second generation of the GraphQL API has more features, and does not pluralize the top-level collection types:

  • Generation 1: post, allPosts
  • Generation 2: post, allPost

The CLI will warn you if there are breaking changes to the schema you deploy and show you where they are. The breaking changes prompt can be disabled by running the deploy command with the --force flag.

Articles affected by this release

Version released on: February 27, 2020