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 publishedAt date (or have a future publishedAt date) 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: true flag 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 userUid is present.
  • Remove duplicates (e.g. both about-us and about-us-uid) by grouping on the base slug. This prevents the same article from appearing twice.
  • Sort by publishedAt to 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:

  1. Always pass includeDrafts and userUid when retrieving slugs or content in preview mode. Without these flags, the SDK will ignore drafts and preview files.
  2. 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.
  3. Sort by publishedAt to ensure that the latest published or preview content appears first. When publishedAt is missing (i.e. the content is draft only), treat the date as 0 so drafts appear at the end or according to your sorting logic.
  4. Use SDK helper functions such as getCMSContentBySlugForLocaleWithDraftSupport to reduce errors. These functions handle much of the complexity for you, including merging draft and preview attributes into a single object.
  5. 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: