Strapi
Strapi + Next.js Preview Mode: Drafts Without Pain
How to show Strapi draft content in Next.js using preview mode and a secure token so editors can preview before publish.

Letting editors preview draft content from Strapi in Next.js requires a way to bypass “published only” and to trigger a preview URL without exposing your API token to the client. Preview mode plus a server-only token does that.
How Strapi returns drafts
Strapi’s REST API returns only published entries for the Public role. To get drafts, you must call the API with a token that has permission to access draft (or use the draft endpoint if your Strapi version provides it). So draft content must be requested from the Next.js server (or a secure API route), not from the browser, and the Strapi token must stay on the server.
Next.js preview mode
Next.js has a preview mode: when enabled (via a route that sets a cookie), the app can behave differently per request—e.g. fetch draft content and disable cache. Create a route (e.g. /api/preview or a Server Action) that accepts a secret, validates it, and calls preview() from next/headers to turn on preview mode, then redirects to the page the editor wants to preview (e.g. the post slug). The secret should be an env var that only your server and Strapi webhook (or your preview link generator) know.
// app/api/preview/route.ts
import { preview } from "next/headers";
import { NextRequest } from "next/server";
export async function GET(request: NextRequest) {
const secret = request.nextUrl.searchParams.get("secret");
const slug = request.nextUrl.searchParams.get("slug");
if (secret !== process.env.PREVIEW_SECRET) {
return new Response("Invalid", { status: 401 });
}
(await preview()).enable();
return Response.redirect(new URL(`/blog/${slug}`, request.url));
}Only call this from a server-side context (e.g. link from Strapi that hits your domain with the secret in the URL, or a webhook that redirects). Don’t expose the secret in client code.
Fetching drafts in the app
In your data layer (e.g. getPostBySlug), check whether preview mode is active (e.g. import { draftMode } from "next/headers" and (await draftMode()).isEnabled). If it is, call Strapi with the server-only token and request the draft (e.g. by id or by slug with draft permission). If not, use the normal public fetch. That way the same page component works for both published and preview; only the data source changes.
import { draftMode } from "next/headers";
export async function getPostBySlug(slug: string) {
const { isEnabled } = await draftMode();
const token = isEnabled ? process.env.STRAPI_PREVIEW_TOKEN : undefined;
const params = new URLSearchParams({ "filters[slug][$eq]": slug });
if (token) params.set("publicationState", "preview");
const res = await strapiFetch(`/posts?${params}`, {
headers: token ? { Authorization: `Bearer ${token}` } : {},
});
// ... return post
}Exact param names depend on your Strapi version; the idea is: when in preview mode, use a token and ask for draft content.
Exit preview and security
Provide an “Exit preview” link that calls a route or action that disables preview mode (e.g. (await preview()).disable() and redirect). Never put the Strapi token or preview secret in client-side code or in URLs that are logged or shared. With that, Strapi + Next.js preview mode gives editors drafts without pain or security risk.
Summary
Use Next.js preview mode (cookie set via a secure route) and a server-only Strapi token to fetch drafts. When preview is enabled, request draft content from Strapi; otherwise use the public API. Keep the secret and token on the server and offer an exit preview link. That’s drafts without pain.