Visual Editor
Preview and edit content in real time — see every change reflected instantly in your running application without leaving the Localess UI.
The Localess Visual Editor opens your frontend application inside an iframe alongside the content form. As you edit fields on the right, the changes are pushed to the preview on the left in real time. Clicking on any marked element in the preview jumps directly to the corresponding field in the form.

How it works
- Localess renders your app inside an iframe in the left panel of the document editor.
- A small sync script loaded by your app opens a message channel to the Localess parent window.
- When a field changes in the form, Localess sends an
inputorchangeevent through the channel. - Your app receives the event, updates its local state, and re-renders — no page reload needed.
- Editable elements marked with
data-ll-idanddata-ll-schemaattributes are highlighted so editors can click them to jump to the right field in the form.
Setup
Integrating the Visual Editor requires three steps in your frontend application.
Step 1 — Load the sync script
The sync script must be loaded in the browser. Use the SDK helper or add the script tag manually.
Via @localess/client:
import { loadLocalessSync } from "@localess/client";
// Call once on the client side, e.g. in your root layout or app entry point
loadLocalessSync("https://my-localess.web.app");Or manually in HTML:
<script
id="localess-js-sync"
type="text/javascript"
async
src="https://my-localess.web.app/scripts/sync-v1.js"
></script>Replace https://my-localess.web.app with your Localess instance origin.
Framework-specific SDKs can load the script automatically. Set enableSync: true when registering the provider:
| SDK | Option |
|---|---|
@localess/react | localessInit({ enableSync: true }) |
@localess/angular | provideLocalessBrowser({ enableSync: true }) |
Step 2 — Mark editable elements
Add data-ll-id and data-ll-schema attributes to the root element of each content block so the Visual Editor can highlight and select it. Add data-ll-field to individual field elements for field-level selection.
Use the helper functions from @localess/client to set these attributes:
import { localessEditable, localessEditableField } from "@localess/client";
// Marks the block root as editable
// Returns: { 'data-ll-id': '...', 'data-ll-schema': '...' }
localessEditable(data)
// Marks a specific field as editable
// Returns: { 'data-ll-field': 'title' }
localessEditableField<MyPage>('title')React example:
const HeroSection = ({ data, links }) => (
<section {...localessEditable(data)}>
<h1 {...localessEditableField<HeroSection>('title')}>{data.title}</h1>
<p {...localessEditableField<HeroSection>('subtitle')}>{data.subtitle}</p>
</section>
);Vue example:
<template>
<section v-bind="localessEditable(data)">
<h1 v-bind="localessEditableField('title')">{{ data.title }}</h1>
</section>
</template>Svelte example:
<section {...localessEditable(data)}>
<h1 {...localessEditableField('title')}>{data.title}</h1>
</section>
localessEditableandlocalessEditableFieldare no-ops outside the Visual Editor iframe — they return empty objects when the sync script is not active, so they are safe to use in production.
Step 3 — Subscribe to edit events
Listen for input and change events from window.localess and update your application state. Always guard the subscription with a browser check — window.localess only exists when the sync script is loaded inside the Visual Editor iframe.
import { isBrowser } from "@localess/client";
if (isBrowser() && window.localess) {
window.localess.on(['input', 'change'], (event) => {
if (event.type === 'input' || event.type === 'change') {
// Replace your server-fetched content with the live editor data
setPageData(event.data);
}
});
}The input event fires on every keystroke for a real-time preview. The change event fires when a field value is confirmed. For most use cases, subscribing to both gives the smoothest editing experience.
Events reference
| Event | When it fires | Payload |
|---|---|---|
input | While a field is being edited (every keystroke) | { type: 'input', data: ContentData } |
change | When a field value is confirmed | { type: 'change', data: ContentData } |
save | When the document is saved | { type: 'save' } |
publish | When the document is published | { type: 'publish' } |
pong | Heartbeat response from the editor | { type: 'pong' } |
enterSchema | Editor cursor enters a schema block | { type: 'enterSchema', id, schema, field? } |
hoverSchema | Editor cursor hovers over a schema block | { type: 'hoverSchema', id, schema, field? } |
SDK integrations
Official SDKs provide built-in Visual Editor support with enableSync and helper utilities. For frameworks without a dedicated SDK, use @localess/client directly following the manual setup steps above.
| Framework | SDK | Guide |
|---|---|---|
| TypeScript / Node | @localess/client | TypeScript SDK |
| Angular | @localess/angular | Angular SDK |
| React / Next.js | @localess/react | React SDK |
| Vue / Nuxt | @localess/client | Vue |
| Svelte / SvelteKit | @localess/client | Svelte |
| Astro | @localess/client | Astro |