usePreviewSubscription not showing draft content in Next.js

9 replies
Last updated: Apr 8, 2021
Maybe I'm missing something in my configuration of the
next-sanity
preview hook, or am misunderstanding how it works, but once set up using
usePreviewSubscription
it should automatically display data in drafts right? Pulling instructions from https://github.com/sanity-io/next-sanity#example-minimal-blog-post-template
I'm trying to test it by editing the title of a blog post without publishing, but am still just getting the published version.
AI Update

I can see you're having trouble getting usePreviewSubscription to show draft content. This is a common issue, and there are several critical pieces that need to be in place for it to work properly.

The Most Common Issue: Missing or Misconfigured Token

The #1 reason drafts don't show up is a missing or improperly configured authentication token. The usePreviewSubscription hook needs a read token with appropriate permissions to access draft content. Check that you're passing it correctly:

const {data} = usePreviewSubscription(query, {
  params,
  initialData,
  token: process.env.SANITY_API_READ_TOKEN, // Must have read permissions
})

Without this token, the subscription can only access published documents, which matches exactly what you're experiencing.

Verify Preview Mode is Actually Enabled

Make sure preview mode is actually active when you're testing. You should have an API route (typically /api/preview) that enables it:

export default function preview(req, res) {
  if (req.query.secret !== process.env.SANITY_PREVIEW_SECRET) {
    return res.status(401).json({ message: 'Invalid token' })
  }
  
  res.setPreviewData({})
  res.redirect(req.query.slug || '/')
}

Then verify you're accessing your site through the preview URL (with the secret parameter), not just the regular published URL. Check your browser dev tools for the __prerender_bypass and __next_preview_data cookies - if they're not present, preview mode isn't active.

The overlayDrafts Pattern

Looking at the next-sanity minimal blog example you referenced, you need to use overlayDrafts in your getStaticProps to merge draft content over published content:

import {overlayDrafts} from './sanity'

export async function getStaticProps({params, preview = false}) {
  const query = groq`*[_type == "post" && slug.current == $slug]`
  const queryParams = {slug: params.slug}
  const docs = await getClient(preview).fetch(query, queryParams)
  
  // This merges drafts with published docs
  const data = overlayDrafts(docs)
  
  return {
    props: {
      data: data[0],
      preview,
      query: preview ? query : null,
      queryParams: preview ? queryParams : null,
    },
  }
}

The overlayDrafts helper is what actually prioritizes draft documents over published ones when both exist. Without this step, your query might return the published version even in preview mode.

Understanding How Drafts Work

When you edit a published document without publishing, Sanity creates a draft version with an ID prefixed by drafts. (e.g., drafts.post-123). Both the published and draft versions exist simultaneously in the Content Lake. The overlayDrafts function intelligently merges these, giving you the draft when it exists and falling back to published when it doesn't.

Quick Debugging Checklist

  • Is preview mode actually enabled? (Check for cookies in dev tools)
  • Are you passing a valid token to usePreviewSubscription?
  • Does your token have read permissions for the dataset?
  • Is getClient(preview) configured to use the token when preview is true?
  • Are you using overlayDrafts() on the fetched data in getStaticProps?
  • Is your draft actually saved? (Check in Studio that it exists with drafts. prefix in the document inspector)
  • Are you accessing the site through the preview URL with the secret parameter?

Modern Alternative: Visual Editing

It's worth noting that usePreviewSubscription is part of an older preview approach. Sanity's current recommended solution is Visual Editing with Draft Mode, which provides a much better experience with:

  • Real-time updates as you type in Studio
  • Clickable overlays linking content to its source
  • Better integration with the Presentation tool

With the modern approach using Draft Mode, you'd use the previewDrafts perspective in your queries, and the framework automatically handles showing draft content when preview mode is active.

The token configuration is almost always the culprit when drafts don't show up. Double-check that it's properly set in your environment variables, has the right permissions, and is being passed through to both your client configuration and the subscription hook.

Show original thread
9 replies
my client config right now:
const config = {
  projectId: process.env.NEXT_PUBLIC_SANITY_PROJECT_ID || '',
  dataset: process.env.NEXT_PUBLIC_SANITY_DATASET || 'production',
  apiVersion: '2021-03-25',
  token: process.env.NEXT_PUBLIC_SANITY_TOKEN,
  useCdn: false,
};
and in my `/blog/[slug].js`:

export default function BlogPost({ post, ...props }) {
  const router = useRouter();
  if (!router.isFallback && !post?.slug) {
    return <FourOhFourPage />;
  }

  const { data: newPost } = usePreviewSubscription(postQuery, {
    params: { slug: <http://props.post?.slug|props.post?.slug> },
    initialData: post,
    enabled: true,
  });

  return <Post post={newPost} {...props} />;
}
My groq query gets
[0]
for the given post slug, but when I remove that in Vision, I can see both the published version, and the
id: 'draft.id-here'
version with my updated title (just added
Test
to the end of the title lol). How do I get the preview client to use that draft version?

  *[_type == 'post' && slug.current == $slug][0] {
    ...,
    'slug': slug.current,
    // other projects here, but not important for this i think
  }
Hmm, looking into it more, it seems to do something when I remove some of the other projections that were references (i.e.
authors[]->{ name }
), but I'm now just getting
null
returned from my
newPost
In your code you've got props.post?.slug should that not be post?.slug?
Ahh good catch! Seems like it was that and maybe some whitespace that was leading to a syntax error
Specifically, when expanding my image asset reference, it didn't like
asset ->
, but
asset->
(no space) fixed it. šŸ¤¦ā€ā™‚ļø
Yes I saw another post saying the new API version doesn't allow for bad syntax any longer.
Specifically, when expanding my image asset reference, it didn’t likeĀ 
asset ->
, butĀ 
asset->
Ā  (no space) fixed it.Ā 
Exactly the same! šŸ˜… Thanks, would never have figured it out by myself! šŸ’

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?