Localess
Node.js

Visual Editor

Enable and subscribe to Localess Visual Editor live-editing events from a plain TypeScript app with @localess/live-preview.

Visual Editor Integration

The browser-side Visual Editor helpers live in @localess/live-preview. They are still re-exported from @localess/client, but those re-exports are deprecated.

npm install @localess/live-preview@latest

loadLocalessSync(origin, options?)

Injects the Localess Visual Editor sync script into the document <head> and resolves once it has loaded. This enables live-editing capabilities when your site is opened inside the Localess Visual Editor. No-op outside a browser or when not running inside an iframe; concurrent calls share one load.

import { loadLocalessSync } from "@localess/live-preview";

await loadLocalessSync('https://my-localess.web.app');

// With the script's debug mode on
await loadLocalessSync('https://my-localess.web.app', { debug: true });
OptionTypeDefaultDescription
debugbooleanfalseTurns on the sync script's debug mode (data-debug): console logging and on-page messages in the preview
sdkstring'@localess/live-preview'Package name of the SDK loading the script, reported to the Visual Editor as data-sdk with this package's version appended — the editor's connection-status tooltip shows it. Framework SDKs set it to their own name

syncScriptUrl()

Returns the URL of the Localess sync script, useful for manual script injection.

const scriptUrl = client.syncScriptUrl();

Marking Editable Content

Use these helpers to add Localess editable attributes to your HTML elements, enabling element selection and highlighting in the Visual Editor.

localessEditable(content)

Marks a content block as editable.

import { localessEditable } from "@localess/live-preview";

// Returns: { 'data-ll-id': '...', 'data-ll-schema': '...' }
<section {...localessEditable(content.data)}>...</section>

localessEditableField<T>(fieldName)

Marks a specific field within a content block as editable, with type-safe field name inference.

import { localessEditableField } from "@localess/live-preview";

// Returns: { 'data-ll-field': 'title' }
<h1 {...localessEditableField<MyPage>('title')}>...</h1>

Listening to Visual Editor Events

When your application is loaded inside the Localess Visual Editor, you can subscribe to editing events via window.localess, which the sync script installs once it has loaded.

// `content` is the document this page fetched
if (window.localess) {
  // Subscribe to input and change — returns an unsubscribe function
  const unsubscribe = window.localess.onChange((event) => {
    // An edit to another document rendered on the page
    if (event.documentId !== content.id) return;
    setPageData(event.data);
  });

  // Subscribe to one event type, or several
  window.localess.on(['save', 'publish'], (event) => {
    if (event.documentId === content.id) console.info(`Content ${event.type}d`);
  });
}

window.localess has these members:

MemberDescription
versionGeneration of the loaded sync script — 'v1' for sync-v1.js
inEditortrue once the Visual Editor has answered the script's handshake, i.e. the page is actually connected — not just loaded in an iframe. Starts false
onChange(callback)Subscribes to input and change. Returns a function that removes the subscription
on(event, callback)Subscribes to one event type or an array of them. Returns a function that removes the subscription
off(event, callback)Removes a callback added with on or onChange

Available Event Types

EventPayloadDescription
input{ type: 'input', documentId: string, data: any }Fired while a field is being edited (real-time)
change{ type: 'change', documentId: string, data: any }Fired when blocks are added, removed, duplicated or reordered — and once when the preview connects, with the current content
save{ type: 'save', documentId: string }Fired when content is saved
publish{ type: 'publish', documentId: string }Fired when content is published
unpublish{ type: 'unpublish', documentId: string }Fired when content is unpublished
pong{ type: 'pong' }Heartbeat response from the editor
enterSchema{ type: 'enterSchema', id, schema, field?, root? }Fired when entering a schema element
hoverSchema{ type: 'hoverSchema', id, schema, field? }Fired when hovering over a schema element
leaveSchema{ type: 'leaveSchema' }Fired when leaving a schema element

documentId is the edited document's Content.id, so a page rendering several documents can apply each edit to the matching one. root: true on enterSchema means the editor went back to the document root, clearing the selection.

Update your Localess deployment together with the SDK: an older deployment sends edits without documentId, so a documentId check like the one above drops every edit.

On this page