Live Preview
See draft content changes reflected on your website in real time before you publish.
How it works
- 1
Duggie CMS generates a short-lived preview token tied to your current editing session.
- 2
The CMS builds a preview URL from your project website URL (set in Settings) with the token appended as a query parameter. You can copy the link or open it directly in a new tab.
https://yoursite.com/?duggiePreviewToken=TOKEN - 3
Your website detects the token and forwards it to /v1/content as the duggiePreviewToken query parameter. The API responds with draft content instead of only published content.
- 4
As you edit assets in the CMS, the preview refreshes to show your latest unsaved changes.
Setup
1. Set your website URL
Go to your project's Settings page and enter your website URL in the Live Preview URL field (e.g. http://localhost:3000 for local dev).
2. Handle the preview token in your website
On page load, check for a duggiePreviewToken in the URL. If present, pass it alongside your Client key. The token instructs the API to return draft content for that session instead of published-only content.
There are three ways to pass the token to the API:
- ✓Header
x-duggie-preview-token: TOKEN(recommended) - 2Query parameter
?duggiePreviewToken=TOKEN - 3Cookie
duggiePreviewToken=TOKEN
// Read token from your server-side request URL
const previewToken = req.query.duggiePreviewToken;
const res = await fetch('/v1/content', {
headers: {
'x-api-key': process.env.DUGGIE_CLIENT_KEY,
// Recommended: pass token as a request header
...(previewToken && { 'x-duggie-preview-token': previewToken })
}
});
const { assets } = await res.json();Token security
Preview tokens arrive as a URL query parameter, which means they can end up in browser history, server access logs, and Referer headers sent to third-party resources (analytics, fonts, CDNs). The token is opaque: a random string with nothing encoded in it, so the link itself reveals nothing about your project or your account. Combined with the short expiry, a leaked token exposes draft content for a few minutes and nothing else. It cannot be used to write, and it stops working the moment the project is deactivated.
For a cleaner integration, follow this pattern on your page load:
- 1Read the token from
?duggiePreviewToken=on page load - 2Store it in
sessionStorageso it survives navigation within the same tab - 3Strip it from the URL with
history.replaceStateso it doesn't appear in browser history or referrer headers - 4Include it as the
x-duggie-preview-tokenheader on API calls for the duration of the session - 5Clear it from
sessionStoragewhen the token expires or the session ends
// On page load: read, store, and strip the token from the URL
const params = new URLSearchParams(location.search);
const token = params.get('duggiePreviewToken');
if (token) {
sessionStorage.setItem('duggiePreviewToken', token);
params.delete('duggiePreviewToken');
history.replaceState({}, '', `${location.pathname}${params.size ? '?' + params : ''}`);
}
// On each API call: read from sessionStorage and pass as a header
const previewToken = sessionStorage.getItem('duggiePreviewToken');
const res = await fetch('/v1/content', {
headers: {
'x-api-key': 'YOUR_CLIENT_KEY',
...(previewToken && { 'x-duggie-preview-token': previewToken })
}
});This is a best practice, not a requirement. Preview works without it. The token in the URL is accepted directly.
Use sessionStorage, not localStorage. Preview sessions are tab-scoped and should not persist across browser restarts.
Preview tokens are short-lived
Tokens expire after the configured TTL (5, 10, or 15 minutes). Once expired, the API returns published-only content. Generate a new token from the Live Preview panel in the asset editor.