Localess
Nuxt

Visual Editor

Live-editing in a Nuxt app — enableSync, LocalessDocument, the useLocalessSync composable, and marking elements editable.

Visual Editor

Set enableSync: true in the localess block of nuxt.config.ts to load the Visual Editor sync script (sync-v1.js):

// nuxt.config.ts
localess: {
  origin: 'https://my-localess.web.app',
  spaceId: 'YOUR_SPACE_ID',
  token: process.env.LOCALESS_PUBLIC_TOKEN, // required — sync runs in the browser
  enableSync: true,
}

Sync needs the public token: the script runs in the browser, so a server-only setup with just serverToken has nothing to sync with. It's a no-op outside the Localess Visual Editor iframe, so it's harmless in a normal browser session — though there's no reason to ship it in a production build that will never be framed by the editor.

LocalessDocument

The whole integration, for most apps. It subscribes to input/change editor events automatically when sync is active and re-renders in place:

<script setup lang="ts">
import { type Content } from '@localess/vue';

import type { Page } from '#shared/models/localess';

const { data: content } = await useAsyncData('content', () => $fetch<Content<Page>>('/api/content'));
</script>

<template>
  <LocalessDocument v-if="content" :document="content" />
</template>

Nothing else is required — the content was fetched server-side with the secret token, and the live updates arrive over the editor bridge with the public one.

useLocalessSync

For an integration that manages its own state instead of handing it to LocalessDocument, subscribe to the bridge directly. It returns a Ref holding the latest matching event:

<script setup lang="ts">
const latest = useLocalessSync(['input', 'change']);
</script>

<template>
  <pre>{{ latest?.data }}</pre>
</template>

The subscription registers in onMounted, so it never runs during SSR. The ref stays undefined when sync isn't active — no enableSync, no public token, or not inside the editor iframe.

useLocalessSync is auto-imported.

Marking elements editable

The editor can only select what it can identify. <LocalessComponent> applies the block-level attributes for you; add field-level ones yourself:

<script setup lang="ts">
import { type LocalessSchemaProps } from '@localess/vue';

import type { Page } from '#shared/models/localess';

const props = defineProps<LocalessSchemaProps<Page>>();
</script>

<template>
  <main v-bind="localessEditable(props.data)">
    <h1 v-bind="localessEditableField<Page>('title')">{{ props.data.title }}</h1>
  </main>
</template>

localessEditable(data) emits data-ll-id and data-ll-schema; localessEditableField(name) emits data-ll-field. Both are auto-imported and return plain attribute objects, so v-bind is all that's needed. Passing the content type as localessEditableField<Page>('title') restricts the argument to that schema's own field names, so a typo is a compile error.

See Visual Editor for what those attributes mean on the editor side.

Available events

EventWhen
inputUser is typing in a field (real-time preview)
changeField value confirmed
saveContent saved
publishContent published
unpublishContent unpublished
pongEditor heartbeat response
enterSchemaEditor cursor enters a schema block
hoverSchemaEditor cursor hovers over a schema block
leaveSchemaEditor cursor leaves a schema block

On this page