Drafts & Previews
When building sites with LeadCMS, you’ll often work with content that is not yet published or that belongs to a single user’s preview session. These situations are handled by two special modes:
- Drafts – content items that do not have a
publishedAtdate (or have a futurepublishedAtdate) and are therefore not visible on the live site. - Temporary previews – user‑specific versions of a page generated by the
preview server while an editor is making changes. These files include the
editor’s UID in the slug and a
draft: trueflag to ensure they are treated as draft content.
This article explains how these modes work and how to design your site code to support drafts and preview pages without introducing duplicates or breaking your content listings.
Understanding drafts vs previews
Drafts
In LeadCMS, a piece of content is considered published only when it has a
publishedAt metadata field set to a date/time in the past. If
publishedAt is missing or set to a future date, the content is treated as a
draft. Drafts are stored locally just like any other content, but by
default the SDK will not return them when you query content for your live
site. This prevents unfinished articles, pages, or products from showing up
before they’re ready.
Drafts are useful for staging new content or scheduling posts. When you’re
ready to publish a draft, add a publishedAt date (or set it to the current
time) and push the content back to LeadCMS. Conversely, to unpublish an
existing article, remove its publishedAt field. The absence of this field
signals to the SDK that the content is draft‑only.
Temporary user previews
While editing a page in the LeadCMS Admin UI, the preview server creates
temporary files so you can see your changes live before saving them. These
preview files have a unique slug that combines the original slug and the
editor’s user UID, separated by a dash. For example, if you edit the
about-us page and your user UID is 4fee1e45-0bb5-4118-82d8-e9d0db51dc4f,
the preview file will be saved as:
about-us-4fee1e45-0bb5-4118-82d8-e9d0db51dc4f
Additionally, preview files include draft: true in their frontmatter or
top‑level JSON. This tells LeadCMS and the SDK to treat the file like a draft
even if a publishedAt value is present. Preview files exist only in the
preview server and are not pushed back to LeadCMS until you save your
changes.
Loading draft and preview content
The LeadCMS SDK provides helper functions to load content correctly in different contexts. When building your site, you usually want to show only published content. When developing a preview mode, you need to include drafts and user‑specific preview files. Here’s how to do both.
Detecting preview slugs and user UIDs
When generating pages dynamically, parse the slug to see if it contains a
user UID. The SDK exposes extractUserUidFromSlug(slug) for this purpose.
If the slug contains a user UID (following the GUID pattern), it returns the
UID; otherwise it returns null.
const userUid = extractUserUidFromSlug(slug)
const includeDrafts = !!userUid
When includeDrafts is true, you should load content with the preview
specific version of the SDK:
const content = getCMSContentBySlugForLocale(slug, locale, includeDrafts)
This call returns the draft or preview file if one exists; otherwise it
returns the published version. When includeDrafts is false, draft
content is ignored.
Listing pages with draft support
For lists of pages (e.g. blog posts or project listings), you need to
retrieve all slugs and then fetch each item. The SDK’s
getAllContentSlugsForLocale(locale, types, includeDrafts, userUid) returns
slugs for the given content types. When includeDrafts is true, the
result includes draft and preview slugs. For each slug, call
getCMSContentBySlugForLocaleWithDraftSupport(slug, locale, userUid, includeDrafts) to load the content and automatically prioritise user
previews or drafts over published versions.
Here’s a typical pattern (simplified from the portfolio demo project):
const includeDrafts = !!userUid
const slugs = getAllContentSlugsForLocale(locale, ["blog-post"], includeDrafts, userUid)
const posts = slugs
.map(slug => getCMSContentBySlugForLocaleWithDraftSupport(slug, locale, userUid, includeDrafts))
.filter((p): p is CMSContent => p !== null)
// Remove duplicate entries (e.g. published and preview versions of the same slug)
.reduce((acc, post) => {
const baseSlug = post.slug.replace(/-[0-9a-fA-F-]{36}$/, "")
if (!acc.find(p => p.slug.replace(/-[0-9a-fA-F-]{36}$/, "") === baseSlug)) {
acc.push(post)
}
return acc
}, [] as CMSContent[])
// Sort by publishedAt so newest content comes first
.sort((a, b) => new Date(b.publishedAt ?? 0).getTime() - new Date(a.publishedAt ?? 0).getTime())
This pattern ensures that you:
- Include drafts and preview files when
userUidis present. - Remove duplicates (e.g. both
about-usandabout-us-uid) by grouping on the base slug. This prevents the same article from appearing twice. - Sort by
publishedAtto show the newest items first.
Loading header and footer previews
In pages where you need to display global components like the header and
footer, pass the userUid to these components. They can then call
loadContentConfigStrict(section, locale, userUid) to load the preview
version of the configuration if one exists. Without userUid, only
published configurations are loaded. This is how the Next.js portfolio demo
ensures that preview edits to the header and footer are visible when
previewing a page.
Best practices to avoid duplicates
When listing dynamic content types (such as blog articles, case studies or projects), failing to account for drafts and preview files can lead to duplicates or missing entries. Follow these guidelines to ensure a smooth experience:
- Always pass
includeDraftsanduserUidwhen retrieving slugs or content in preview mode. Without these flags, the SDK will ignore drafts and preview files. - Group by base slug when listing content in preview mode. Use a
regular expression to remove the user UID suffix (e.g.
/-[0-9a-fA-F-]{36}$/) and keep only the latest version (draft or published). This avoids showing both the published and preview versions of the same page. - Sort by
publishedAtto ensure that the latest published or preview content appears first. WhenpublishedAtis missing (i.e. the content is draft only), treat the date as0so drafts appear at the end or according to your sorting logic. - Use SDK helper functions such as
getCMSContentBySlugForLocaleWithDraftSupportto reduce errors. These functions handle much of the complexity for you, including merging draft and preview attributes into a single object. - Avoid referencing preview slugs directly in links unless you’re intentionally building a preview interface. In normal site navigation, always link to the base slug (without the UID).
Following these practices ensures that your preview environment faithfully reflects a user’s edits without polluting your production site or breaking pagination and filtering logic.
Summary
Draft and preview handling is an essential part of a modern content workflow. LeadCMS lets you work on drafts privately and preview your changes in real time without affecting the published site. By detecting preview slugs, loading content with the appropriate flags and grouping your lists by base slug, you can avoid duplicates and deliver a seamless editorial experience. Use the SDK helper functions and sort logic demonstrated above to build robust draft and preview support into your projects.
Next steps
With drafts and preview logic in place, you're ready to set up a preview server or move to deployment. Continue with: