Directus Content Versioning for Draft Previews

This guide belongs to Directus Data Layer Patterns and solves a common Directus editing problem: how to prepare and preview changes to an item that is already published, without affecting the live site until the changes are ready. Directus content versioning lets editors create a version of an item, edit it freely, preview it, and promote it to the main item when approved. The guide covers enabling versions, configuring preview URLs, fetching a version in the frontend’s draft mode and promoting it with revalidation.

With only a status field, a published item has one state: editing it changes the live page as soon as it is saved. Teams work around this by duplicating items or saving changes elsewhere, which breaks references and history. Content versions keep the main item live and untouched while one or more named versions hold the pending changes.

Editing a published item through a versionAn editor creates a version of a published article, edits it, and opens the preview URL; the frontend's draft route fetches the version with the preview token and renders it; after review the editor promotes the version, the main item updates, and a flow revalidates the public page.EditorDirectusFrontend draft routePublic pagecreate version 'spring-update'edit versionopen preview URL (version key)read item with version (preview token)render versionpromote versionflow → revalidate article
The live page changes only at promotion, and the preview shows exactly what promotion will publish.

The Problem

A university’s course pages were published items in Directus. When a department prepared next year’s course descriptions, editors either changed the live pages months early or copied items into a “drafts” collection and pasted content back later, losing relations to modules and instructors. Preview only worked for items in draft status, so there was no way to see next year’s version of a live course page before publishing it.

How Content Versioning Works

Enable versioning per collection. In the collection settings, turn on content versioning. Editors then see a version menu on each item.

Versions hold changes, not copies. A version stores the changes relative to the main item. Reading an item with a version key returns the main item merged with the version’s changes, so the frontend receives the same shape as usual.

Preview URLs per collection. Configure a preview URL template on the collection, including the item key and the version key, pointing at the frontend’s draft route. The studio opens it from the item view.

Promotion. When the version is ready, editors promote it, which applies its changes to the main item. That is a normal update event, so the revalidation flow updates the public page.

Status field versus content versionsA status field and content versioning compared on previewing new items, previewing changes to live items, multiple pending changes and effect on the live page.NeedStatus fieldContent versionsPreview a new itemdraft statusalso worksPreview changes to a live itemedits go liveversion stays separateSeveral pending change setsnonamed versionsLive page while editingchanges on saveunchanged until promote
Most sites use both: status for new items, versions for changes to published ones.

Implementation

Configure the collection’s preview URL in the studio with placeholders for the key and version, for example:

Text
https://www.example.com/api/draft?collection=courses&key={{id}}&version={{$version}}&secret=PREVIEW_SECRET

The secret here is a short preview secret that only enables draft mode; it is not a Directus token. The draft route checks it, enables draft mode, stores the version key in a cookie and redirects to the page.

TypeScript
// app/api/draft/route.ts
import { draftMode, cookies } from "next/headers";
import { redirect } from "next/navigation";
import { pathForItem } from "@/lib/routes";

export async function GET(req: Request) {
  const url = new URL(req.url);
  if (url.searchParams.get("secret") !== process.env.PREVIEW_SECRET) return new Response("invalid", { status: 401 });
  const collection = url.searchParams.get("collection")!;
  const key = url.searchParams.get("key")!;
  const version = url.searchParams.get("version");

  (await draftMode()).enable();
  const jar = await cookies();
  if (version && version !== "main") jar.set("directus_version", version, { httpOnly: true, secure: true, sameSite: "none", path: "/" });
  else jar.delete("directus_version");

  const path = await pathForItem(collection, key);                  // from the route manifest
  if (!path?.startsWith("/")) return new Response("unknown item", { status: 404 });
  redirect(path);
}

The data layer reads the version from the cookie in draft mode and passes it to Directus with the preview token.

TypeScript
// lib/directus/course.ts
import { draftMode, cookies } from "next/headers";
import { createDirectus, rest, staticToken, readItems } from "@directus/sdk";

export async function getCourse(slug: string) {
  const isDraft = (await draftMode()).isEnabled;
  const version = isDraft ? (await cookies()).get("directus_version")?.value : undefined;
  const token = isDraft ? process.env.DIRECTUS_PREVIEW_TOKEN! : process.env.DIRECTUS_READ_TOKEN!;
  const client = createDirectus(process.env.DIRECTUS_URL!).with(staticToken(token)).with(rest());

  const [course] = await client.request(readItems("courses", {
    filter: { slug: { _eq: slug } },
    fields: ["id", "title", "slug", "description", { modules: ["id", "title"] }],
    limit: 1,
    ...(version ? { version } : {}),                               // merged view of main item + version
  }));
  return course ?? null;
}

Draft-mode requests bypass all caches, so editors see changes to the version as soon as they save and refresh. Published requests use the read-only token, never a version, and stay cached.

Promotion and revalidation

Promoting a version updates the main item, which fires items.update and runs the revalidation flow described in Directus flows for revalidation. Nothing special is needed on the frontend. Restrict promotion to roles that are allowed to publish, since promoting is effectively publishing.

Exiting draft mode

Editors move between preview and the public site, and a sticky draft mode causes confusion: an editor who previewed a version yesterday still sees that version today and assumes it is live. Add a visible banner in draft mode that names the version being shown, with a link to leave draft mode, which clears the draft cookie and the version cookie and returns to the public page. Give the draft cookies a short lifetime, a few hours, so they expire on their own. When the editor opens a different item’s preview URL, the draft route replaces the version cookie, so the previous version never leaks into another item’s preview. These small details matter more than they seem: most reports of “preview shows the wrong thing” come from stale draft cookies rather than from bugs in fetching.

For teams that use live preview inside the studio, the same route works in the preview pane. Make sure the frontend allows being framed by the Directus studio’s origin through its content security policy, and nothing else, so preview works without opening the site to framing by arbitrary pages.

Configuration Reference

Setting Recommendation Why
Versioning enabled on collections with live edits Changes stay separate until promoted.
Preview URL draft route with key, version and preview secret One click from the studio.
Version storage httpOnly cookie in draft mode Persists across preview navigation.
Token preview token only in draft mode Versions are not public.
Caching bypass in draft mode Editors see saves immediately.
Promotion restricted to publishers Promotion equals publishing.

Gotchas & Edge Cases

  • Relations inside versions. Changes to related items are not part of the version unless they are changes to the relation itself. Preview shows current related items.
  • Several versions. Editors can create more than one version; the preview shows whichever version key the URL carries. Name versions clearly.
  • Cookies in the studio iframe. Live preview inside the studio uses an iframe on another origin; cookies need SameSite=None; Secure, as in the example.
  • Stale versions. A version prepared months ago may conflict with later changes to the main item. Review the merged result before promoting.

Worked Example

The university enabled versioning on course and programme collections. Departments created a version per course for the coming academic year in spring, previewed the full new pages through the draft route, and promoted all versions on the day the new catalogue went live. Relations to modules and instructors stayed intact, the live pages remained correct until the switch, and the promotion day required no content copying.

Work to prepare next year's course pagesEditor hours spent preparing and publishing next year's course pages with copied draft items and with content versions.Copied draft items120 editor hoursContent versions45 editor hours
Versions removed the copying and pasting on both ends.

Combining Versions with Scheduled Publishing

Many version workflows end on a known date, such as the start of a season or a product launch. Directus does not promote versions on a schedule by itself, but a scheduled flow can: a flow with a schedule trigger runs at the chosen time, reads items whose version is marked ready and whose release date has passed, and promotes them through the API. Each promotion fires the usual update events and revalidations. Keep the list of scheduled promotions visible to editors, for example as a field on the item or a small dashboard, and send a notification after the run with successes and failures, so a problem on launch morning is noticed immediately rather than by readers.

Rollout Checklist

  • Enable content versioning on collections where published items change.
  • Configure preview URLs with key, version and a preview secret.
  • Store the version in an httpOnly cookie in draft mode and fetch with the preview token.
  • Bypass caches for draft requests.
  • Let promotion trigger the normal revalidation flow.
  • Restrict promotion to publishing roles.

Frequently Asked Questions

Do versions replace the status field?

No. Status still controls whether new items are published at all. Versions handle changes to items that are already live.

Can the public site ever show a version?

Not directly. Versions are visible only through the preview token in draft mode; the public site always shows the main item until a version is promoted.

What happens to a version after promotion?

It is applied to the main item and removed or kept according to Directus’s behaviour for your version; the main item’s revision history records the change.

Does versioning affect API performance?

Reading with a version key merges changes on the fly, which is fast for single items. It is only used in draft mode anyway, so public traffic never pays any cost for it.