Skip to content
brix.v2 stable

Search documentation

70 guides

Documentation menu

Contentful Live Preview

Give editors a protected, standalone preview for adapter-backed entries, with inspector attributes and live updates from Contentful's official SDK.

Enable the preview entry

Enable the generated browser integration:

// vite.config.ts
import { defineBrixConfig } from "brix/vite";

export default defineBrixConfig({ contentfulPreview: true });

Configure credentials only on the server:

export const brixServer = defineBrixServer(brix, {
  env,
  contentfulPreview: (c) => ({
    accessToken: c.env.CONTENTFUL_PREVIEW_TOKEN,
    secret: c.env.CONTENTFUL_PREVIEW_SECRET,
    spaceId: c.env.CONTENTFUL_SPACE_ID,
    targetOrigin: "https://app.contentful.com",
  }),
});

Use static options for one space or a callback for request-specific tenants. The callback runs only on the preview route. environment defaults from the URL. Set host only for a trusted Preview API endpoint; custom token hosts require the same explicit trusted-host opt-in as the Delivery client. targetOrigin accepts one HTTPS origin or an array when editors use another Contentful host.

Configure Contentful preview URLs

Configure each supported content type with its literal content type ID:

https://example.com/_/contentful/preview/{locale}/{env_id}/card/{entry.sys.id}?secret=<CONTENTFUL_PREVIEW_SECRET>

Contentful replaces {locale}, {env_id}, and {entry.sys.id}. Replace card yourself for each model. The route verifies the secret and adapter support before fetching the entry through the Preview API. Responses use no-store and no-referrer, and only the preview route relaxes framing for configured Contentful origins.

Never put the Preview API token in the URL, browser props, or adapter fallback data. The URL contains only the separate preview secret and should still be treated as sensitive.

Make adapters preview-ready

Use preview.getProps(fieldName) to add inspector attributes to the element that displays a field. Normal production rendering has no preview context, so no inspector attributes or controls are emitted.

Adapters with required extra props or loader data provide deterministic standalone fallbacks:

import { propsOf } from "brix/contentful/rendering";

export const CardListAdapter = createAdapter({
  contentType: "cardList",
  props: propsOf<{ appearance: "light" | "dark" }>(),
  load: async (entry, c, signal) => loadCards(entry, c, signal),
  preview: {
    props: { appearance: "light" },
    data: { cards: [] },
    layout: "padded",
  },
  component: ({ entry, loaded, preview, props }) => (
    <CardList
      heading={entry.fields.heading}
      headingAttributes={preview?.getProps("heading")}
      appearance={props.appearance}
      cards={loaded.status === "ok" ? loaded.data.cards : []}
    />
  ),
});

The standalone preview uses fallback data rather than server loader results. Keep it nonsecret and deterministic. The generated integration subscribes to untransformed entry updates, applies inspector mode, and can render adapter-defined select controls.