How to Differentiate between an Unpublished Document and a Published Draft from the API
Yes! You can differentiate between an unpublished document and a published draft using perspectives and document IDs.
The key distinction is:
- Unpublished document: Only exists as a draft with ID like
drafts.post-123(no published version exists) - Published draft: Both versions exist -
post-123(published) anddrafts.post-123(draft with changes)
Using Perspectives
The most straightforward approach is to use the published perspective when querying. This returns only published documents:
const client = createClient({
// ... config
perspective: 'published'
})With this perspective:
- Unpublished documents won't appear at all
- Published documents appear even if they have drafts
Checking in Your Query Results
You can also check programmatically by looking at the _id:
- If
_idstarts withdrafts., it's a draft version - If the same document ID exists both with and without the
drafts.prefix, it's a published document with draft changes - If only
drafts.{id}exists (no corresponding published version), it's unpublished
GROQ Queries for Detection
To find only unpublished documents (never been published):
*[_id in path("drafts.**") && count(*[^._id == "drafts." + _id]) == 0]To find published documents with draft changes:
*[count(*[_id in [^._id, "drafts." + ^._id]]) > 1]To exclude all drafts from production queries:
*[_type == 'post' && !(_id in path("drafts.**"))]The previewDrafts perspective is also useful for preview environments - it automatically prioritizes drafts over published versions when both exist, which is perfect for showing the latest changes before they go live.
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.