Video Production Plan : Video 8 — Live Preview Architecture and Preview Routing

Text LessonReleased: June 7, 2026

Video 8 — Live Preview Architecture and Preview Routing

AttributeDetails
Course4 (Preview, Visual Builder, and Releases), Module 4.1 (lessons 1-3)
CoversLessons 4.1.1, 4.1.2, 4.1.3
PriorityCritical
Length18-25 min
FormatScreencast (code editor + Contentstack UI side-by-side)
StatusNot started

Why This Video Matters

Preview is one of the places where a visual explanation helps much more than text alone. Headless architectures do not give you preview for free — it requires deliberate implementation.

Outline

  1. Why Live Preview matters: editors need to see changes before publishing; without it, they fly blind
  2. The two-delivery-plane mental model: preview token vs delivery token
  3. Requirements: what your frontend needs to support (SDK initialization with live preview config, preview token, host URL)
  4. Draft vs published routing: how to serve draft content in preview mode and published content in production
  5. Show the configuration in Contentstack settings and the corresponding frontend code
  6. SSR preview pattern: server fetches draft content on each request
  7. CSR preview pattern: client-side SDK listens for changes and re-renders in real time
  8. onEntryChange, edit tags, and preview transport
  9. Walk through a working Live Preview implementation step by step
  10. Common failure modes: CORS issues, wrong environment tokens, caching interfering with draft content

Key Lines

"Headless architectures do not give you preview for free."

"Preview is a product capability, not a side feature."

"If preview behavior is inconsistent, editorial trust erodes quickly."

Detailed Talking Points

1. Why Live Preview matters

  • Headless CMS architectures do not provide preview out of the box. Preview is an architecture concern, not a UI toggle.
  • Without preview, editors "fly blind" -- they publish content hoping it looks right, or they ask developers to check for them. Both are slow and error-prone.
  • Preview is a correctness system. When an editor asks "is this page ready?", they are asking a systems question: can I trust what I see before I hit publish?
  • Unreliable preview leads to two failure modes: teams publish too cautiously (slow velocity) or too optimistically (broken pages in production).
  • Frame preview as a product capability, not a nice-to-have. Treat preview correctness as a release criterion.

2. The two-delivery-plane mental model

  • Split content delivery into two planes: the published plane (optimized for live user traffic, CDN-cached, Delivery Token) and the preview plane (optimized for draft validation, no caching, Preview Token).
  • If you treat preview as "published plus a flag," you get inconsistent results. Preview has different host routing, request context, and caching expectations.
  • Published traffic goes to cdn.contentstack.io (or your region's CDA host). Preview traffic goes to rest-preview.contentstack.com (or your region's preview host).
  • Both tokens are scoped to an environment. The difference: the Delivery Token only returns published entries. The Preview Token returns the latest saved version of every entry, regardless of publish state.
  • The Preview Token does not bypass environments -- it bypasses the publish gate. An entry does not need to be published for the Preview Token to return it.

3. Requirements: SDK initialization and preview config

  • Three things your frontend needs: the Live Preview SDK (@contentstack/live-preview-utils), a Preview Token, and the correct preview host URL for your region.
  • Initialize the Delivery SDK with live_preview config: enable: true, preview_token, and host pointing to your region's preview endpoint.
  • Initialize ContentstackLivePreview.init() separately with ssr: true or ssr: false (matching your rendering model), mode: "builder", stackSdk, stackDetails, and clientUrlParams.host pointing to your region's Contentstack app URL.
  • The clientUrlParams.host must match the Contentstack web app for your region (e.g., eu-app.contentstack.com for AWS EU). Getting this wrong causes silent failures.
  • Use @timbenniks/contentstack-endpoints to resolve correct base URLs for any region string -- avoids hardcoding the wrong host.
  • The editButton config with exclude: ["outsideLivePreviewPortal"] ensures the floating edit button only appears inside the Contentstack preview iframe.

4. Draft vs published routing

  • Two hosting approaches: separate preview host (recommended) or single host with mode switching.
  • Separate host: www.example.com runs with Delivery Token, preview.example.com runs with Preview Token. Same codebase, different env vars. Complete isolation.
  • Single host with mode switching: detect preview mode via URL param (?live_preview), cookie, or header. Cheaper infrastructure but riskier -- a bug in mode-switching logic could expose draft content to production visitors.
  • Preview URL structure must mirror production URL structure exactly. If production serves /blog/q3-report, preview must serve /blog/q3-report at the same path. Otherwise editors land on 404s.
  • In Contentstack, configure the preview base URL under Settings > Live Preview. The CMS constructs preview URLs by combining this base URL with the entry's URL path.

5. Configuration walkthrough (Contentstack settings + frontend code)

  • Show Settings > Tokens: where to create Preview Token and Delivery Token.
  • Show Settings > Live Preview: where to set the preview URL base and enable Live Preview for the stack.
  • Show the .env.production vs .env.preview files side by side: same API key, same environment, different tokens and PREVIEW=true/false flag.
  • Show the SDK initialization code: how the live_preview block conditionally enables preview based on the env var.
  • Emphasize: both deployments share the same Git repository. The only difference is environment variables injected at build/deploy time.

6. SSR preview pattern

  • Server fetches draft content on each request using the Preview Token. The server renders HTML and sends it to the browser.
  • The Live Preview SDK initializes on the client with ssr: true. When the editor modifies content, the SDK triggers a page refresh via router.refresh() (Next.js App Router) or window.location.reload().
  • router.refresh() is strongly preferred: it re-runs server components without a full page reload, preserving scroll position and client state.
  • SSR preview is structurally simpler -- no client-side data management. The trade-off is latency: each update requires a server round-trip (typically 200-500ms).
  • Critical: caching must be explicitly disabled for preview requests. In Next.js App Router, fetch() is cached by default. Set cache: "no-store" or next: { revalidate: 0 } for preview fetches, or editors will see stale content.

7. CSR preview pattern

  • The browser fetches content directly from Contentstack and renders it in the DOM. Updates are instant and data-driven.
  • Initialize the SDK with ssr: false. Register ContentstackLivePreview.onEntryChange(callback) -- the callback fires every time the editor modifies a field.
  • When onEntryChange fires, the SDK has already intercepted the Delivery SDK instance. The re-fetch returns real-time draft data from the postMessage payload -- no network call. The component re-renders instantly.
  • CSR preview gives the best editor experience: sub-second updates, no page flicker, no scroll position loss.
  • For hybrid pages (SSR page shell + CSR interactive components), keep SSR-fetched fields on the server path and CSR-fetched fields on the client path. Never mix data sources for the same field -- it creates synchronization bugs where server HTML and client updates disagree.

8. onEntryChange, edit tags, and preview transport

  • onEntryChange is the primary hook for responding to Live Preview updates. Its behavior depends on the ssr flag.
  • With ssr: false: callback gets updated data instantly from the postMessage payload. No network round-trip.
  • With ssr: true: callback triggers a page refresh so the server re-fetches with the updated live_preview hash.
  • Edit tags are data-cslp attributes on DOM elements. Format: {content_type_uid}.{entry_uid}.{locale}.{field_path}. They map rendered content to CMS fields for field-level highlighting and in-place editing.
  • The postMessage bridge handles all communication between the Contentstack entry editor (parent window) and your app (iframe): handshake, entry changes, hash updates, and navigation events.
  • You never implement postMessage handling yourself -- the SDK manages it. But knowing it exists explains why Live Preview requires an iframe context and why CORS can block it.

9. Walk through a working implementation step by step

  • Start from zero: create tokens, configure Live Preview settings, set up env vars, initialize SDKs.
  • Show the full request flow: editor opens Live Preview, CMS loads preview URL in iframe, SDK detects iframe context, SDK intercepts API calls and redirects to preview host with Preview Token.
  • Demonstrate a content change: editor types a new headline, postMessage fires, onEntryChange triggers, page updates (instant for CSR, server round-trip for SSR).
  • Show the live_preview hash in action: without the hash, preview API returns last saved draft. With the hash, it returns real-time editing state including unsaved changes.
  • Show edit tags lighting up on hover: the data-cslp attributes enable field-level highlighting so editors can see exactly which DOM element maps to which CMS field.

10. Common failure modes

  • CORS issues: the Contentstack app and your preview deployment are on different origins. If your server blocks cross-origin iframe embedding or postMessage, Live Preview silently fails. Check X-Frame-Options and CSP headers.
  • Wrong environment tokens: using a Delivery Token in the preview deployment means editors only see published content. This often goes unnoticed during setup because testing happens with already-published entries. The bug surfaces when an editor creates a brand-new entry and preview shows a 404.
  • Caching interfering with draft content: CDN caching preview responses, browser caching from a previous production visit, or Next.js fetch cache serving stale data. All three produce the same symptom: editor saves changes, refreshes, sees old content.
  • Missing or wrong preview host configuration: hardcoding the wrong region's preview host, or forgetting to set clientUrlParams.host to the correct Contentstack app URL. Both cause silent failures.
  • SSR state leakage: storing preview state globally in a long-lived server process. One editor's draft context leaks into another editor's request, producing non-deterministic preview results.
  • Wrong ssr flag: initializing with ssr: false in an SSR app causes the SDK to intercept client-side calls that never fetch data. Result: confusing flicker where server HTML shows old content, client briefly shows new content, then hydration conflicts.

Screen: What to Show

Outline itemWhat to show on screen
Opening (outline items 1-2)

Contentstack entry editor: Show an entry with unpublished draft changes. Point out the "Save" vs "Publish" distinction. Click Live Preview to open the preview panel -- show how it loads the preview URL in an iframe.

Whiteboard or slide: Two-plane diagram. Left side: "Published Plane" with Delivery Token arrow to 

cdn.contentstack.io

. Right side: "Preview Plane" with Preview Token arrow to 

rest-preview.contentstack.com

. Keep this visible as a reference throughout. 

SDK setup (outline items 3, 5)

Contentstack dashboard: Navigate to Settings > Tokens. Show where Preview Token and Delivery Token are created. Highlight that both are scoped to an environment.

Contentstack dashboard: Navigate to Settings > Live Preview. Show the Preview URL field and the enable toggle.

Code editor (split view): Show 

.env.production

 and 

.env.preview

 side by side. Highlight the differences: 

PREVIEW_TOKEN

 present only in preview, 

PREVIEW=true

 only in preview.

Code editor: Show the SDK initialization code. Highlight 

live_preview.enable

live_preview.preview_token

live_preview.host

. Then show 

ContentstackLivePreview.init()

 with 

ssr

mode

stackSdk

clientUrlParams.host

Routing (outline item 4)

Browser: Open 

www.example.com/blog/post

 and 

preview.example.com/blog/post

 in side-by-side tabs. Show that the published version shows published content, the preview version shows draft content including unpublished changes.

Code editor: Show the middleware or env-var logic that switches between Delivery Token and Preview Token based on mode. 

SSR pattern (outline item 6)

Code editor: Show the Next.js App Router server component fetching content with 

draftMode()

. Show the 

cache: "no-store"

 setting on the fetch call.

Code editor: Show the client component with 

ContentstackLivePreview.init({ ssr: true })

 and 

onEntryChange(() => router.refresh())

.

Live demo: Edit a field in the Contentstack entry editor and show the SSR preview update in real time. Point out the slight delay from the server round-trip. 

CSR pattern (outline item 7)

Code editor: Show the React hook with 

onEntryChange(fetchEntry)

 and 

ssr: false

.

Live demo: Edit a field and show the instant client-side re-render. Compare the speed to the SSR pattern. 

Edit tags and transport (outline item 8)

Code editor: Show 

data-cslp

 attributes on DOM elements. Explain the format: 

content_type.entry_uid.locale.field_path

.

Browser DevTools: Inspect an element in the preview iframe and show the 

data-cslp

 attribute. Hover over content in the preview panel and show the field-level highlighting. 

Working implementation walkthrough (outline item 9)Full-screen code editor + preview panel: Walk through the complete flow from token creation to a working Live Preview. Show the live_preview hash in the network tab -- the SDK sends it with each request.
Failure modes (outline item 10)

Browser DevTools (Console tab): Show a CORS error when 

X-Frame-Options

 blocks the iframe. Show how to fix it.

Browser DevTools (Network tab): Show a preview request returning published content because the wrong token was used. Show the 

access_token

 header vs 

preview_token

 header.

Browser DevTools (Network tab): Show a cached response with 

Cache-Control: public

 on a preview request. Show the fix: 

no-store

 headers.

Veda Scenario Thread

Veda is the fictional luxury jewelry brand used throughout this certification course. Use it as the running example in this video.

  • Opening: Veda's content team just hired a new marketing editor. She needs to preview a new product page for the "Matrix Link Bracelet" before publishing. Without Live Preview, she would have to publish to staging, check the page, then unpublish if something is wrong. That is a broken workflow.
  • Two-plane model: Veda runs two deployments: www.veda.com (production, Delivery Token, CDN-cached) and preview.veda.com (preview, Preview Token, no caching). Same Next.js codebase, different env vars.
  • SDK initialization: Show the Veda project's lib/contentstack.ts file. Walk through how the region is set to eu (Veda is a European brand), and how getContentstackEndpoints("eu", true) resolves the correct preview and app hosts for AWS EU.
  • Draft vs published routing: The editor opens the "Matrix Link Bracelet" product entry in Contentstack. She clicks Live Preview. Contentstack loads preview.veda.com/products/matrix-link-bracelet in the iframe. The preview deployment fetches draft content using the Preview Token.
  • SSR pattern: Veda's product pages are server-rendered for SEO (title, description, structured data). The server fetches draft content on each preview request. router.refresh() handles updates.
  • CSR pattern: Veda's "Related Products" carousel is client-rendered. It uses onEntryChange to re-render in place when the editor changes related product references.
  • Edit tags: The editor hovers over the product title on the preview page. The data-cslp="product.blt_matrix_link_001.en-us.title" attribute lights up, showing her exactly which field maps to that heading. She clicks and edits in place.
  • Failure mode demo: Show what happens if Veda's preview deployment accidentally uses the Delivery Token -- the new "Matrix Link Bracelet" entry (never published) returns a 404 in preview. Fix it by swapping to the Preview Token.

Transitions

  1. Item 1 to 2: "So if preview is not free, how does Contentstack actually separate draft content from published content? It comes down to two delivery planes."
  2. Item 2 to 3: "Knowing the model is one thing -- now let us look at what your frontend code needs to support it."
  3. Item 3 to 4: "The SDK is initialized, but how does your app decide when to use the Preview Token versus the Delivery Token? That is routing."
  4. Item 4 to 5: "Let me show you exactly where this is configured -- both in Contentstack's dashboard and in the frontend code."
  5. Item 5 to 6: "Configuration is done. Now let us see how preview actually works at runtime, starting with server-side rendering."
  6. Item 6 to 7: "SSR preview works, but it has a round-trip delay. Client-side rendering gives you instant updates -- here is how."
  7. Item 7 to 8: "Both patterns rely on the same underlying mechanisms: onEntryChange, edit tags, and postMessage transport. Let us look at those."
  8. Item 8 to 9: "Now that you understand all the pieces, let us put them together in a complete working implementation."
  9. Item 9 to 10: "Before we wrap, let me show you the most common ways Live Preview breaks -- so you can avoid them."
  10. Closing to Video 9: "Live Preview gives editors visibility into draft content. But seeing content is only half the story -- in the next video, we will look at Visual Builder, which lets editors actually edit content in place, directly on the page."

Common Mistakes to Call Out

  • Using the Delivery Token in the preview deployment. Editors only see already-published content. New entries and draft changes are invisible. The bug hides during setup because developers test with already-published entries. It surfaces when an editor creates a new entry and gets a 404.
  • Applying production cache rules to the preview host. CDN, browser, and framework fetch caches all need explicit no-cache configuration for preview. One missed layer means editors see stale content and lose trust in the entire preview system.
  • Mismatched URL paths between preview and production. If preview uses a different routing scheme (e.g., /preview/blog/slug instead of /blog/slug), Contentstack cannot construct the correct preview URL. Editors land on 404 pages.
  • Setting ssr: false in an SSR application. The SDK tries to intercept client-side SDK calls that never actually fetch data. Result: server HTML shows old content, client briefly flashes new content, then hydration conflicts cause unpredictable behavior.
  • Forgetting to disable fetch caching in SSR preview mode. In Next.js App Router, fetch() is cached by default. Without cache: "no-store" on preview requests, the server returns stale published content even with a Preview Token.
  • Using window.location.reload() instead of router.refresh() in Next.js. Full page reload discards client state, resets scroll position, and forces a complete HTML re-parse. router.refresh() re-runs server components smoothly.
  • Storing preview state globally in a long-lived server process. One editor's draft context leaks into another editor's request, producing non-deterministic preview. Preview state must be request-scoped.
  • Missing clientUrlParams.host configuration. The SDK needs the Contentstack app URL for your region to establish the postMessage bridge. Without it, Live Preview silently fails to communicate with the entry editor.

Notes

Use this space for recording notes, script drafts, or post-production feedback.