Lesson 76 of 81
Before Draft Mode existed, the Pages Router solved the exact same 'show one editor the unpublished draft' problem with an API-route-and-cookie pair called Preview Mode. If you're maintaining older Next.js code, this is what you'll find instead.
11 min read
Every option explains itself — why the right one is right, and why each wrong one is wrong. Miss one and it comes back later on a spaced-repetition schedule.
This topic is about judgment and practical tradeoffs more than a single right answer — a mock interview is the better way to test yourself on it.
Be interviewed on this →Hints without spoilers, and a plain explanation of why a wrong answer was wrong — answered only from this lesson. Free account, no card.
Sign up freeWhat interviewers actually ask about this, and what a strong answer has to cover.
A strong answer covers
Likely follow-ups
Preview Mode is the Pages Router's original answer to the same problem the App Router's Draft Mode solves: a CMS editor wants to see unpublished content before everyone else does, without disabling caching site-wide. It predates Draft Mode by several major versions and works through a different, older API surface — res.setPreviewData() and res.clearPreviewData() in an API route, and a preview boolean handed to getStaticProps as part of its context argument.
The underlying mechanism is nearly identical to Draft Mode's: a cookie set on the editor's browser flips a per-request flag, which getStaticProps can read and branch on — fetch the draft content instead of the published content, skip the cache, whatever the CMS integration needs. Everyone without the cookie keeps getting the normal statically generated page.
// pages/api/preview.ts
import type { NextApiRequest, NextApiResponse } from "next";
export default function handler(req: NextApiRequest, res: NextApiResponse) {
const { secret, slug } = req.query;
if (secret !== process.env.CMS_PREVIEW_SECRET || !slug) {
return res.status(401).json({ message: "Invalid token" });
}
// Optionally pass data through to getStaticProps via the second argument
res.setPreviewData({});
res.writeHead(307, { Location: `/${slug}` });
res.end();
}
// pages/[slug].tsx
import type { GetStaticProps } from "next";
export const getStaticProps: GetStaticProps = async ({ params, preview }) => {
const content = preview
? await fetchDraftContent(params!.slug as string)
: await fetchPublishedContent(params!.slug as string);
return { props: { content, preview: preview ?? false } };
};
res.setPreviewData({}) sets two cookies: __prerender_bypass (the same bypass mechanism Draft Mode still uses under the hood) and __next_preview_data, which can carry a small signed payload — useful for something like "which draft version" without a separate lookup. getStaticProps receives preview: true in its context on any subsequent request from that browser, and branches accordingly.
getStaticProps or a preview context field at all. A codebase mixing both routers (a real, common migration-in-progress state) needs Draft Mode for anything under app/ and Preview Mode for anything still under pages/; they don't share an implementation, only a similar cookie-based idea.res.setPreviewData() can only be called from an API route (pages/api/*), never from getStaticProps itself. The enabling step and the data-fetching step are always two separate requests: hit the API route first, which redirects into the actual page second.setPreviewData() is signed and size-limited — it's meant for a small identifier (a draft ID, a version token), not for stashing the entire draft content in the cookie itself.res.clearPreviewData() — there's no single toggle; entering and exiting are two distinct routes, exactly mirroring Draft Mode's enable()/disable() split.pages/ directory in active use, Preview Mode is the correct, working API for it — not a legacy trap to route around."This codebase has a pages/ directory using getStaticProps with a preview flag — how does that relate to Draft Mode you've used elsewhere?" tests whether a candidate can recognize that two differently-named APIs are solving the identical problem with the identical cookie mechanism underneath — a real situational awareness gap for anyone who's only ever worked in App Router codebases and assumes Draft Mode is the only implementation that ever existed.