Skip to content
Strife Docs

Reference Live preview

Custom Live Preview implementation

No matter what front-end framework you are using, you can build your own hook using the same underlying tooling that Strife provides.

First, install the @strifeapp/strife base package:

Terminal
npm install @strifeapp/strife

This packages provides you with the following functions:

PathDescription
subscribe
Subscribes to the the Strife studio's window.postMessage events and calls the provided callback function.

The subscribe function takes the following arg:

PathDescription
callback
A callback function that is called with data every time a change is made to the document.

With this function, you can build your own hook using your front-end framework of choice:

JavaScript
import { subscribe } from '@strifeapp/strife'

const onChange = (data) => { ... }
const unsubscribe = subscribe(onChange);
// To unsubscribe, call `unsubscribe();`

// To build your own hook, subscribe to Live Preview events using the`subscribe` function
// It handles everything from:
// 1. Listening to `window.postMessage` events
// 2. Calling the `onChange` callback with the result
// Your hook should also:
// 1. Handle the results of the `onChange` callback to update the UI
// 2. Unsubscribe from the `window.postMessage` events when it unmounts

Callback data shape

The data argument passed to your callback is a plain object representing the current document state, keyed by each editor's propertyName. For a template defined as:

TypeScript
const Homes: Template = {
  displayName: 'Home',
  collection: 'Homes',
  editors: [
    { label: 'Heading',     editor: { name: 'str-input',    type: 'text', propertyName: 'heading' } },
    { label: 'Description', editor: { name: 'str-textarea', type: 'text', propertyName: 'description' } },
  ],
};

the callback receives:

TypeScript
subscribe((data) => {
  // data.heading: string        — current value of the Heading editor
  // data.description: string    — current value of the Description editor
});

Every editor's propertyName becomes a top-level key on data. Nested types (e.g. objects from str-image-group or arrays from str-multi-select) follow the same shape as they appear in the stored document.

Hover highlighting and stegaClean

While a document is open in the editor's live preview, moving the pointer over your page outlines the element under it on the page, and a click on it opens the matching field in the editor: the tab it sits on, the chapter it belongs to, an image's editing card. To make that work for a site that renders the state itself, the editor hides each text field's path inside the text value as zero-width characters (the same technique as Vercel's Content Link and Sanity's Visual Editing). They carry no glyph and no width, HTML escaping leaves them alone, and a published site never receives them: only the editor's preview sends them.

They are still characters, so a comparison against a preview value can fail:

JavaScript
import { subscribe, stegaClean } from '@strifeapp/strife'

subscribe((data) => {
  data.heading === 'Welcome';             // false in the preview: the value starts with the hidden path
  stegaClean(data.heading) === 'Welcome'; // true
  const clean = stegaClean(data);         // a deep copy with every hidden path removed
});

Use stegaClean on any preview value you compare, match a route against, or measure (length). Values a site is likely to compare are never encoded to begin with: slugs, dates, numbers, option codes of pickers and radio groups, labels, keywords and SEO fields. Only text, textarea and rich text values carry a path, plus an image's alt text (its URL is never touched).

Rendering needs no change: print the values as they come, and the pointer resolves the field from the text under it, or from an image's alt attribute. A click keeps its effect on your page — nothing is prevented. If your components already carry data-field attributes (the SDK's own web components do), those win over the hidden path.

PathDescription
stegaClean
Removes the hidden field paths from a string, or recursively from an array or plain object.
stegaDecode
Returns the field path hidden in a string, or null. Useful for building your own hover affordance.

Initialization timing

Strife Studio performs a handshake with the preview iframe at page load. For subscribe to receive events, the SDK must be present in the page before that handshake completes — meaning before your framework has finished hydrating client components.

Load the SDK eagerly at the top of your application, via either of these patterns:

npm install + import in your entry-point (recommended for build-tool projects):

JavaScript
// src/main.js (or main.ts, index.js, wherever your app boots)
import "@strifeapp/strife";

CDN tag in your HTML <head> (simplest when you don't have a build step):

HTML
<script type="module" src="https://unpkg.com/@strifeapp/strife"></script>

Components can then import subscribe as normal — the ESM module is cached and resolves to the already-initialized singleton.