Docs

Draft preview

Open any draft entry in your live Astro site — without making it publicly visible — using a shared preview token.

How it works

  1. Generate a Preview token in Settings → API.
  2. Set a Preview URL for a collection in Schema → (collection). Use {slug} as a placeholder.
  3. The editor's ↗ Preview button opens https://yoursite.com/posts/{slug}?preview_token=….
  4. The button doesn't put the master token in the URL: the admin signs a short-lived token (about an hour) for exactly that collection and slug, so a leaked link expires and can't preview other entries. Restricted editors can only get tokens for collections they may open. The master token itself still works if you build preview links by hand.
  5. Your Astro page calls getPreviewEntry() with the token — which reads directly from the pod, bypassing the published-only snapshot.

Astro page setup

In a server-rendered Astro page (output: 'server' or 'hybrid'):

---
import { getEntry, getPreviewEntry } from 'orbiter:collections';

const slug  = Astro.params.slug!;
const token = Astro.url.searchParams.get('preview_token') ?? '';

const entry = token
  ? await getPreviewEntry('posts', slug, token)
  : await getEntry('posts', slug);

if (!entry) return Astro.redirect('/404');
---
<article>
  <h1>{entry.data.title}</h1>
  <div set:html={entry.data.body} />
</article>

getPreviewEntry validates the token against the one stored in your pod. If the token is wrong or missing it returns null — so unauthenticated visitors never see draft content.

Security

  • The token is a 48-character hex string generated with crypto.getRandomValues. Regenerating it in Settings immediately invalidates any existing preview links.
  • Only one token exists at a time — it is shared across all editors who have access to the admin.
  • Preview URLs should only be shared with trusted reviewers.
  • The set:html={entry.data.body} example above renders rich-text content as-is — sanitize it (e.g. with DOMPurify) before rendering if any non-admin can edit entries, so a malicious field value can't run as script in your visitors' browsers.

TypeScript

The generated orbiter-env.d.ts includes typed overloads per collection:

import { getPreviewEntry } from 'orbiter:collections';

// Returns PostEntry | null
const entry = await getPreviewEntry('posts', slug, token);

Last updated Edit this page on GitHub ↗