How to query Sanity documents to show only one item when both draft and published exist?

1 replies
Last updated: Jan 5, 2021
I’d love some help understanding how to work with drafts and published documents. I’m running into issues with including drafts in queries because documents that are published but also have unpublished changes return two separate items,
<_id
and
drafts.<_id>
. How can I achieve something similar to the studio’s documents list pane, where each item shows only one time whether or not it’s published and there’s an edit icon if there are changes?
AI Update

The issue you're experiencing is a common challenge when working with Sanity's draft system. When a document is published and then edited, you end up with two separate documents in the Content Lake: one with _id and another with drafts._id, which causes duplicates in your query results.

The solution is to use the previewDrafts perspective when configuring your Sanity client. This perspective automatically handles the draft/published overlay logic for you, returning only one document per item (prioritizing the draft version when both exist).

Using the previewDrafts Perspective

When you set up your Sanity client, specify the perspective:

import {createClient} from '@sanity/client'

const client = createClient({
  projectId: 'your-project-id',
  dataset: 'your-dataset',
  useCdn: false, // Important: CDN doesn't support perspectives
  apiVersion: '2024-01-01',
  perspective: 'previewDrafts', // This does the magic!
})

With previewDrafts, your queries will automatically return a single document per item, just like the Studio's document list pane. If a draft exists, you get the draft; if not, you get the published version.

Manual GROQ Approach

If you need more control or can't use perspectives, you can manually overlay drafts in your GROQ queries using scoring:

*[_type == 'post'] 
  | score(_id in path("drafts.**")) 
  | order(_score desc, _id) 
  | {
      ...,
      "isDraft": _id in path("drafts.**")
    }

This prioritizes draft documents by giving them a higher score. You can then group or deduplicate results in your application code.

Filtering Out Drafts in Production

For production environments where you only want published content, use the published perspective or filter drafts explicitly:

*[_type == 'post' && !(_id in path("drafts.**"))]

Framework-Specific Solutions

If you're using Gatsby, the gatsby-source-sanity plugin has an overlayDrafts option that handles this automatically in development mode.

The key takeaway: perspectives (particularly previewDrafts) are Sanity's built-in solution for exactly this problem. They give you the same draft-overlay behavior that the Studio uses internally, so you get consistent results whether you're in the Studio UI or querying via the API. This is the recommended approach rather than manually filtering or merging drafts in your queries.

Show original thread
1 reply
Finally found it! For anyone else looking for this in the future, you can find the
collate
util implemented here in the source code: https://github.com/sanity-io/sanity/blob/f4b66a8695b30d387a2de97d3737252616854a54/[…]nity/desk-tool/src/panes/documentsListPane/DocumentsListPane.js

Sanity – Build the way you think, not the way your CMS thinks

Sanity is the developer-first content operating system that gives you complete control. Schema-as-code, GROQ queries, and real-time APIs mean no more workarounds or waiting for deployments. Free to start, scale as you grow.

Was this answer helpful?