The new Cosmic dashboard is here!
Test drive it now →

How to use Next.js Preview Mode with the Cosmic headless CMS

Community Articles
Community Articles How to use Next.js Preview Mode with the Cosmic headless CMS

Static web page generation is practical when fetching data from a headless CMS. However, when adding new content to our CMS after our site is built, we will want a way to preview draft content that is not yet ready to be published.

Using Next.js Preview Mode, we solve this issue by rendering pages at request time, allowing us to see unpublished content that was not present during static generation. Let’s explore how we can use Next.js Preview Mode with a headless CMS to preview draft content for ourselves and collaborators.

Using Preview Mode in the Cosmic Dashboard

In this example, we’re using Cosmic as our headless CMS. You can check out the live demo and clone the Developer Portfolio template GitHub repository.

Setting our secret preview token and URL

To ensure only those with access can see our preview URLs, let’s create a string for our secret preview token. You can use this API key generator to get a base64 secret string, or just use a simple text string that you create.

Now we set the custom preview URL, which will be located at ‘pages/api/preview.js’ in our Next.js project.

  https://<YOUR-SITE>/api/preview?secret=<YOUR_SECRET_PREVIEW_TOKEN>&slug=[object_slug]

Setting this preview URL in Cosmic is easy. We can navigate to the settings of an Object Type, and set the Preview Link using the convention above. For testing during development, set to localhost, otherwise set it to your desired URL. Using Cosmic, set the slug parameter to [object_slug]. Cosmic will automatically convert this short code to the slug of a given Object.

Setting the preview link in the Cosmic dashboard

Getting the preview post by slug

First, let’s create an async function to fetch the preview post data from our headless CMS using the Cosmic NPM module.

const Cosmic = require('cosmicjs')
const api = Cosmic()

const bucket = api.bucket({
  slug: YOUR_BUCKET_SLUG,
  read_key: YOUR_READ_KEY,
})
const is404 = error => /not found/i.test(error.message)

export async function getPreviewPostBySlug(slug) {
  const params = {
    query: { slug },
    status: 'any',
    props: 'slug',
  }

  try {
    const data = await bucket.getObjects(params)
    return data.objects[0]
  } catch (error) {
    // Don't throw if a slug doesn't exist
    if (is404(error)) return
    throw error
  }
}

By setting the status param to any, we fetch all unpublished Objects with a draft status as well as the latest draft of a published Object upon request.

Creating the preview API route

Now that we’ve configured the secret preview token in our CMS, we create an API route that checks that the token matches and then fetches the unpublished post we requested with the preview URL we set earlier (if it exists).

Once the token matches and if the slug we requested exists in our CMS, we enable Preview Mode and set the cookies with res.setPreviewData({}) using a Temporary Redirect to the location of our unpublished post.

export default async function preview(req, res) {
  // Check the secret and next parameters
  // This secret should only be known to this API route and the CMS
  if (
    req.query.secret !== process.env.COSMIC_PREVIEW_SECRET ||
    !req.query.slug
  ) {
    return res.status(401).json({ message: 'Invalid token' })
  }

  // Fetch the headless CMS to check if the provided `slug` exists
  const post = await getPreviewPostBySlug(req.query.slug)

  // If the slug doesn't exist prevent preview mode from being enabled
  if (!post) {
    return res.status(401).json({ message: 'Object not found' })
  }

  // Enable Preview Mode by setting the cookies
  res.setPreviewData({})

  // Redirect to the path from the fetched post
  // We don't redirect to req.query.slug as that might lead to open redirect vulnerabilities
  res.writeHead(307, { Location: `/posts/${post.slug}` })
  res.end()
}

Updating getStaticProps

Now that we have set the Preview Mode cookies with res.setPreviewData, getStaticProps will be called upon request.

On the page where we render our preview post, getStaticProps will accept an object as an argument, which will be the unpublished data we fetched from our headless CMS.

export async function getStaticProps({ params, preview = null }) {
  const data = await getPostAndMorePosts(params.slug, preview)
  return {
    props: {
      preview,
      post: {
        ...data.post,
      },
    },
  }
}

Using Preview Mode in Cosmic

Now that we’ve set up our API routes and functionality for preview mode, using it with Cosmic is as easy as clicking a button. Cosmic will take the preview URL we set up earlier and generate a query for each Object we create, automatically adding the Object slug to the end of it.

Using Preview Mode from the Cosmic dashboard

Exiting Preview Mode

We can create one more API route to exit Preview Mode. We are simply going to clear the preview cookies, and redirect to the home page.

export default async function exit(_, res) {
  // Exit the current user from "Preview Mode". This function accepts no args.
  res.clearPreviewData()

  // Redirect the user back to the index page.
  res.writeHead(307, { Location: '/' })
  res.end()
}

Since we are setting the cookies for the current session of our application, we can treat this data as a context for the entire app. In the live demo used in this tutorial, I’ve created a banner that displays when the application is in Preview Mode. In the banner, we then use a Next.js Link to route to the exit-preview API route, clearing the preview mode cookies and bringing the application back to the home page.

Exiting Preview Mode

Whether you are wanting to share new content with your team, or simply want to see your own content before it goes live, using Next.js Preview Mode is a solid solution for doing so. Sharing content is as easy as providing the preview URL with your team, or clicking a button in Cosmic.

I hope you found this article useful. You can tune into our new show Build Time, where we cover everything from headless CMS, Next.js, React, and much more.

You may also like


I’m going to demonstrate how to get started integrating your Cosmic-powered content with your favorite third-party applications using Zapier.
In this tutorial, we're going to build a digital shrine to the great musical artists of our time and to listening to music by album - the way great artists meant it to be listened to! In the process, you'll learn a little bit about Cosmic, React, CSS Grid, Flexbox, Material UI and Spotify's aweso
We recently released Cosmic Functions (public beta). We're excited to help teams build amazing modern products together with new serverless solutions.
Metadata is extremely important for SEO as it speaks to search engines directly from each page crawled, to communicate important information or request a specific action from the search engine. Cosmic provides an easy modal to manage media metadata.
This article walks through an approach to build a web application using Angular and Cosmic to help generate marketing leads for your team.

Get started with Cosmic

Personal projects are free. Scale up as you grow.
Start building Talk to sales