LLM Reference

LLM Reference: Preview Listener

This is a dense reference for LLMs working with Vextro’s live preview system. The preview listener runs on the public site (inside the iframe) and receives field updates broadcast by the admin panel’s VextroLivePreview sidebar.

Import Path

import { initVextroPreviewListener } from 'vextro/preview';
import type { VextroPreviewEventDetail } from 'vextro/preview';

Types

type VextroPreviewEventDetail = {
  data: Record<string, unknown>;  // All field values from the admin form
  collectionSlug?: string;        // Collection the document belongs to
  documentId?: string;            // ID of the document being previewed
};

// Internal message shape (handled automatically by initVextroPreviewListener)
// _collectionSlug and _id are nested inside data, not top-level
type VextroPreviewMessage = {
  type: 'vextro-preview-update';
  data: Record<string, unknown>;
};

initVextroPreviewListener(): () => void

Attaches a message event handler to window. Returns a cleanup function that removes the listener.

On each incoming vextro-preview-update message:

  1. Finds elements with data-vextro-field matching keys in the data payload
  2. Updates textContent by default, or updates the attribute named by data-vextro-attr
  3. Fires a vextro:preview-data CustomEvent on window for custom handling

The module auto-initializes the listener on import. Do not call initVextroPreviewListener() after any import from vextro/preview — any import (named or side-effect) triggers module-level auto-initialization, and calling the function manually after that registers a second listener.

// Correct: auto-init runs when the module loads
import 'vextro/preview';

The function is exported for environments where ES module side-effects are suppressed (SSR, test runners) and manual initialization is required. In normal browser usage, rely on auto-init.


DOM Attributes

data-vextro-field="<fieldName>"

Marks an element as a preview target. The listener updates textContent with the matching field value.

<h1 data-vextro-field="title">Placeholder</h1>
<p data-vextro-field="excerpt">...</p>

data-vextro-attr="<attrName>"

When present alongside data-vextro-field, the listener updates the named attribute instead of textContent.

<img data-vextro-field="heroImageUrl" data-vextro-attr="src" alt="" />
<a data-vextro-field="ctaUrl" data-vextro-attr="href">Read more</a>

vextro:preview-data Custom Event

Fires on window for every incoming preview message. Use this for fields requiring custom handling (rich text, nested data, arrays).

import type { VextroPreviewEventDetail } from 'vextro/preview';
import { renderTipTapToHtml } from 'vextro/content';

window.addEventListener('vextro:preview-data', (e: CustomEvent<VextroPreviewEventDetail>) => {
  const { data, collectionSlug, documentId } = e.detail;
  if (data.body) {
    document.querySelector('#body').innerHTML = renderTipTapToHtml(data.body);
  }
});

Admin Collection Config

Enable the preview panel for a collection by adding a preview option to defineVextroCollection:

export const posts = defineVextroCollection({
  slug: 'posts',
  preview: {
    url: 'https://yoursite.com/posts/{slug}',
  },
});

Curly-brace placeholders (e.g. {slug}, {_id}) are replaced with the current document’s field value before the iframe loads.


Security Notes

  • The listener accepts messages from any origin (preview may differ from admin domain).
  • initVextroPreviewListener never writes data back to Convex or the admin.
  • Safe to include in production builds — the listener is a no-op when no admin message is received.
  • The vextro:preview-data custom event detail does not expose origin. For origin-restricted environments, add a native window.addEventListener('message', handler) and check event.origin there.
Previous
Content Rendering