Localess
Nuxt

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()
TokenserverToken (secret)token (public)
Runs inServer only — Nitro routes, server/ utilitiesAnywhere the Vue plugin is installed
Can read draft contentYesNo
Throws ifCalled in the browser, or serverToken unsettoken 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' });

Setting version: 'draft' on the module's public config would send the flag from the browser with a public token, which the API rejects. Gate drafts behind a server route instead — 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>
PropTypeRequiredDescription
dataContentDataYesContent data object. The component looks up data._schema in the registry
linksLinksNoResolved content links map, forwarded to the rendered component
referencesReferencesNoResolved references map, forwarded to the rendered component
assetsAssetsNoResolved 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, the fallbackComponent renders if configured, and otherwise an inline error naming the missing key. 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>
PropTypeRequiredDescription
documentContent<T>YesThe 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.

On this page