Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/alineacms/alinea/llms.txt

Use this file to discover all available pages before exploring further.

Alinea’s preview system embeds your application in an iframe inside the dashboard so editors can see content changes in real time before they publish. When an editor opens the preview, Alinea signs a JWT preview token and redirects the iframe to your site via the handler’s preview redirect endpoint, which enables Next.js Draft Mode and sets the token as a cookie. From that point on, every edit the editor makes is pushed to the page as a live patch without requiring a full reload.

Enabling previews in config

Set preview: true in your CMS config to activate the preview feature:
src/cms.ts
import {Config} from 'alinea'
import {createCMS} from 'alinea/next'
import * as schema from './schema'

export const cms = createCMS({
  schema,
  preview: true,
  handlerUrl: '/api/cms',
  baseUrl: {
    development: 'http://localhost:3000',
    production: 'https://example.com'
  },
  workspaces: {
    main: Config.workspace('Main', {
      source: 'content',
      mediaDir: 'public',
      roots: {
        pages: Config.root('Pages', {contains: [schema.Page]}),
        media: Config.media()
      }
    })
  }
})
The preview option accepts:
preview
boolean | ComponentType<{entry: Entry}>
Set to true to enable previews using the default URL strategy derived from baseUrl. Set to a React component to provide a fully custom preview renderer that receives the live entry object as a prop.

Rendering the preview widget

Add the floating preview widget to your root layout so it appears when an editor opens a page in the dashboard iframe. Call cms.previews() — it returns null when Draft Mode is disabled, so the widget is invisible to regular site visitors:
app/layout.tsx
import {cms} from '@/cms'

export default async function RootLayout({children}: {children: React.ReactNode}) {
  const Preview = await cms.previews({widget: true})

  return (
    <html>
      <body>
        {children}
        {Preview}
      </body>
    </html>
  )
}
cms.previews() accepts a PreviewProps object:
widget
boolean
When true, renders the floating <alinea-preview> widget that shows connection status and provides a direct link back to the entry being edited in the dashboard. When false or omitted, the hook still subscribes to live patches but does not render any visible UI.
workspace
string
The workspace key to use when constructing the “edit this entry” link in the widget. Omit to use the default workspace.
root
string
The root key to use when constructing the “edit this entry” link. Omit to use the default root.
cms.previews() is an async server component helper. It reads the Next.js draftMode() and cookies() headers to determine whether Draft Mode is active. The <NextPreviews> component it returns is loaded with next/dynamic and ssr: false so it only runs in the browser — the widget registers itself with registerPreviewWidget() and listens for patch messages from the dashboard iframe.

How the preview handshake works

  1. The editor clicks Preview in the dashboard.
  2. Alinea creates a signed JWT containing the target URL and opens your app in an iframe at GET /api/cms?preview=<token>.
  3. The handler verifies the token, calls draftMode().enable(), and issues a 302 redirect to the target URL.
  4. The page re-renders in Draft Mode. cms.resolve() detects the draft mode cookie and switches to preferDraft status so unpublished edits are included.
  5. The <alinea-preview> widget establishes a postMessage channel with the parent dashboard frame.
  6. Each subsequent edit in the dashboard sends a patch via postMessage. The widget applies it via setPreviewCookies() and triggers router.refresh() to re-render the server components with the updated content.

Draft workflow with enableDrafts

When enableDrafts: true is set in your config, every content change goes through a draft state before it can be published:
src/cms.ts
export const cms = createCMS({
  // ...
  enableDrafts: true,
  preview: true
})
With drafts enabled, editors must explicitly publish an entry to commit it to the repository. Draft changes are stored in the backend database (Cloud or your own) and are visible in Draft Mode preview but not to regular visitors.

baseUrl and preview routing

baseUrl tells Alinea where your application is hosted so it can construct preview redirect URLs. You can provide a single string or split development and production URLs:
src/cms.ts
export const cms = createCMS({
  // Single URL
  baseUrl: 'https://example.com',

  // Or environment-specific
  baseUrl: {
    development: 'http://localhost:3000',
    production: 'https://example.com'
  }
})
baseUrl is required in production. Without it, requestContext throws at runtime because it cannot construct the handler URL used for preview redirects and API calls.

Preview status in the widget

The <alinea-preview> widget reflects the current connection state via the livePreview attribute:
StateMeaning
"connected"Live patching is active
"loading"A patch is being applied and the router is refreshing
"warning"Patch was received but setPreviewCookies failed (usually a cross-origin cookie restriction)
undefinedDraft Mode is enabled but no patch has arrived yet
Previews work in both development and production environments. In development, the Alinea CLI dev server serves the dashboard and patches flow through the local Next.js server. In production, the dashboard is served from the static admin.html file and patches flow through your deployed handler at handlerUrl. Alinea Cloud and self-hosted backends both support live previews.

Build docs developers (and LLMs) love