Next.js

Setting up Next.js Draft Mode

Static rendering is useful when your pages fetch data from a headless CMS. However, it’s not ideal when you’re writing a draft on DatoCMS and want to view the draft immediately on your page.

Next.js has a feature called Draft Mode, which solves this problem. Here’s a guide on how to use it.

(Applies to Next.js 15.x and 16.x)

Next.js 15 and later

Since Next.js 15, draftMode() is an async function that returns a Promise. Always await it. The examples below use this API.

Step 1: Create a draft mode API route

First, create a route handler that enables draft mode. It can have any name — e.g. app/api/draft-mode/enable/route.ts. In this route handler, you must call (await draftMode()).enable() to enable draft mode.

To keep drafts private, protect the route with a secret token. To support Visual Editing, also accept a redirect parameter with the path to load:

app/api/draft-mode/enable/route.ts
import { draftMode } from 'next/headers';
import { redirect } from 'next/navigation';
import type { NextRequest } from 'next/server';
export async function GET(request: NextRequest) {
const token = request.nextUrl.searchParams.get('token');
const redirectTo = request.nextUrl.searchParams.get('redirect') || '/';
// Ensure that the request comes from a trusted source
if (token !== process.env.SECRET_API_TOKEN) {
return new Response('Invalid token', { status: 401 });
}
// Avoid open redirects: only follow paths on this same host
if (new URL(redirectTo, request.url).hostname !== request.nextUrl.hostname) {
return new Response('URL must be relative!', { status: 422 });
}
const draft = await draftMode();
draft.enable();
redirect(redirectTo);
}

You can manually test this route by accessing it via a browser at http://localhost:3000/api/draft-mode/enable?token=<SECRET_API_TOKEN>. You’ll notice that you'll be redirected to the homepage, and the __prerender_bypass cookie will be set.

To exit draft mode, create a similar route that calls (await draftMode()).disable().

Pro tip: Previews inside DatoCMS

The Web Previews plugin loads your site inside an iframe. Browsers block the default __prerender_bypass cookie in that context. The Next.js Starter Kit re-sets the cookie with sameSite: 'none', secure: true and partitioned: true to make draft mode work in the iframe. See its complete enable and disable routes.

Step 2: Conditionally include draft records

Once draft mode is set up, your pages can check whether it is active with the isEnabled property of await draftMode(). Use it to turn on the includeDrafts flag in the call to performRequest.

This will instruct DatoCMS to return records at their latest version available instead of the currently published one:

import { draftMode } from 'next/headers';
export default async function Page() {
const { isEnabled } = await draftMode();
const { data: { homepage } } = await performRequest(PAGE_CONTENT_QUERY, {
includeDrafts: isEnabled,
});
// [...]
}
Cache Components

If your app sets cacheComponents: true, you can read isEnabled inside a 'use cache' function too. In draft mode, Next.js re-runs cached functions on every request and does not save their results. Thus, editors always see the latest draft.

You can read more details regarding draft mode on the Next.js docs page.

Last updated: