Content
Fetch content server-side with the secret token or client-side with the public one, and render it by schema, in a Nuxt app.
Two clients, two tokens
The module's token split gives you two ways to reach the API, and which one you use decides what you can read:
useLocalessServerClient() | useLocaless() | |
|---|---|---|
| Token | serverToken (secret) | token (public) |
| Runs in | Server only — Nitro routes, server/ utilities | Anywhere the Vue plugin is installed |
| Can read draft content | Yes | No |
| Throws if | Called in the browser, or serverToken unset | token unset |
Prefer the server client. It keeps the read off the browser entirely, and it's the only way to reach draft or unpublished content — a public token is scoped to published content and translations.
Server-side fetching
useLocalessServerClient() is exposed through the #localess/server alias, which the module registers in both the app and Nitro build graphs:
// server/api/content.ts
import { useLocalessServerClient } from '#localess/server';
export default defineEventHandler(async event => {
const client = useLocalessServerClient();
return client.getContentBySlug((getQuery(event).slug as string) || 'home');
});Then read it from a page with Nuxt's own data layer:
<script setup lang="ts">
const { data: content } = await useAsyncData('content', () =>
$fetch('/api/content', { query: { slug: 'home' } })
);
</script>
<template>
<LocalessDocument v-if="content" :document="content" />
</template>Nuxt's payload transfer hydrates the server-fetched result to the client, so the browser doesn't re-fetch — the module needs no hydration mechanism of its own.
The client is memoised for the lifetime of the server process, so its in-memory cache is shared across requests. That's safe because every caller uses the same token and therefore has identical permissions. Tune it with the cacheTTL module option, or set cacheTTL: false to disable caching.
Draft content
Reading unpublished content needs the secret token, so it only works through the server client:
const client = useLocalessServerClient();
const draft = await client.getContentBySlug('home', { version: 'draft' });There's no module-level version option. Request drafts per call from a server route with useLocalessServerClient(), as above, so the draft flag never leaves the server — see Draft & Publish.
Client-side fetching
useLocaless() returns the client the module's plugin installed, authenticated with the public token. Use it for reads that genuinely have to happen in the browser — an event handler, a search box, a paginated list:
<script setup lang="ts">
const client = useLocaless();
async function loadMore(slug: string) {
return client.getContentBySlug(slug);
}
</script>useLocaless is auto-imported. It throws if no public token was configured, which is the expected outcome of a deliberately server-only setup.
Rendering content
LocalessComponent
Renders one content block by looking up its _schema in the component registry. Auto-imported, so no import line is needed:
<template>
<LocalessComponent
v-for="block in props.data.blocks ?? []"
:key="block._id"
:data="block"
:links="props.links"
:references="props.references"
/>
</template>| Prop | Type | Required | Description |
|---|---|---|---|
data | ContentData | Yes | Content data object. The component looks up data._schema in the registry |
links | Links | No | Resolved content links map, forwarded to the rendered component |
references | References | No | Resolved references map, forwarded to the rendered component |
assets | Assets | No | Resolved content assets map, forwarded to the rendered component |
It always applies localessEditable(data)'s attributes to the rendered component's root, so the Visual Editor can target it.
If a schema key has no registered component, an inline error naming the missing key renders in its place — Nuxt has no fallback-component option. The DevTools Localess tab shows every discovered key, which is usually the fastest way to spot the mismatch.
LocalessDocument
Renders a whole Content response, unpacking data/links/references/assets for you, and re-renders live on Visual Editor edits. This is what a page should render:
<template>
<LocalessDocument :document="content" />
</template>| Prop | Type | Required | Description |
|---|---|---|---|
document | Content<T> | Yes | The full content response from getContentBySlug/getContentById |
Reassigning document re-syncs the rendered tree, so a client-side navigation to a different slug swaps the content correctly even though Nuxt reuses the same component instance.
Use LocalessDocument for the top-level content of a page, and LocalessComponent for nested blocks inside an already-rendered tree. See Visual Editor for the live-sync half.
Getting Started
Nuxt module for Localess — one config block for content delivery, component auto-registration, a server-only client for draft content, and Visual Editor integration.
Visual Editor
Live-editing in a Nuxt app — enableSync, LocalessDocument, the useLocalessSync composable, and marking elements editable.