Localess
Next.js

Getting Started

Use Localess in a Next.js app — App Router with React Server Components, or a static export.

Package: @localess/react

The @localess/react package is the official React integration for the Localess headless CMS platform. In a Next.js app you pick the export that matches your rendering mode:

Rendering modeExportLive editing
App Router with React Server Components@localess/react/rscYes, driven by a Server Action
Static export (output: 'export')@localess/react/ssrYes, via the client-side fallback

Both are covered below. The rest of the API — components, hooks, rich text — is identical across them.

⚠️ Security Notice: Use a secret token when localessInit() runs exclusively in server-only code that is never bundled to the browser, which is the case for both walkthroughs on this page. Use a public, read-only token wherever localessInit() runs in code that ships to the browser — that is the static-export fallback's second, client-side call (see Visual Editor).

Requirements

  • Node.js >= 24.0.0
  • React 17, 18, or 19

Installation

# npm
npm install @localess/react@latest

# yarn
yarn add @localess/react@latest

# pnpm
pnpm add @localess/react@latest

Setup

Call localessInit once, in a module of its own, to configure the client, register your components, and optionally enable the Visual Editor. It returns the client, so export it from that module and import it wherever you fetch:

// src/shared/localess.ts
import { localessInit } from "@localess/react/rsc";

import { Page } from "@/components/localess/page";
import { Button } from "@/components/localess/button";

export const localessClient = localessInit({
  origin: "https://my-localess.web.app",
  spaceId: "YOUR_SPACE_ID",
  token: "YOUR_API_TOKEN",
  enableSync: process.env.LOCALESS_ENABLE_SYNC === 'true', // on for preview deployments only
  components: {
    'Page': Page,
    'Button': Button,
  },
});
// anywhere that fetches
import { localessClient } from "@/shared/localess";

const content = await localessClient.getContentBySlug<Page>('home', { locale });

Putting the call in a module that your pages import is what guarantees it has run before anything reaches the registry. A bare localessInit() side effect in a root layout is not enough on its own under the App Router, where a page module can be evaluated without the layout's module body having run first.

If you'd rather not thread the client through imports, getLocalessClient() returns the same instance from anywhere after initialization — see Fetching content.

Schema keys must match _schema exactly. The examples here use 'Page'/'Button' because that's how the schemas are named in the space they came from. Use whatever casing your own CMS uses — there is no normalization on a hand-written map.

Initialization Options

OptionTypeRequiredDefaultDescription
originstringYes—Fully qualified domain with protocol
spaceIdstringYes—Localess Space ID, found in Space settings
tokenstringYes—Localess API token (keep secret — server-side only)
version'draft'No—Set 'draft' to fetch unpublished content by default; omit for published content
debugbooleanNofalseEnable debug logging
cacheTTLnumber | falseNo300Cache TTL in seconds. Set false to disable caching entirely
componentsRecord<string, React.ElementType>No{}Map of schema keys to React components
fallbackComponentReact.ElementTypeNo—Component rendered when a schema key has no registered component
enableSyncbooleanNofalseLoad the Visual Editor sync script for live-editing support

Keep enableSync off on public production deployments. Live editing under @localess/react/rsc runs through a Server Action, and a Server Action is a public endpoint. The SDK checks every call server-side and drops it unless the server's own localessInit() enabled sync, the document ID was rendered by that server, and the event type, path and data are well-formed. Enable it only where editors work — usually a preview deployment that the Visual Editor points at.

Writing components

Components receive data, links, and references as props — type them with LocalessSchemaProps<T>, the contract every registered component accepts (there's no base class to extend, unlike @localess/angular's SchemaComponent<T>). Always spread localessEditable/localessEditableField so the Visual Editor can highlight and select the block and its fields:

import { localessEditable, localessEditableField } from "@localess/react";
import type { LocalessSchemaProps } from "@localess/react";
import type { HeroSection } from "./.localess/localess";

const Hero = ({ data, links, references }: LocalessSchemaProps<HeroSection>) => (
  <section {...localessEditable(data)}>
    <h1 {...localessEditableField<HeroSection>('title')}>{data.title}</h1>
    <p {...localessEditableField<HeroSection>('subtitle')}>{data.subtitle}</p>
  </section>
);

Pass links and references through the entire tree — child LocalessComponents need them.

localessEditable(content)

Marks a content block root element as editable.

import { localessEditable } from "@localess/react";

const Header = ({ data }) => (
  <nav {...localessEditable(data)}>
    {/* ... */}
  </nav>
);

localessEditableField<T>(fieldName)

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

import { localessEditableField } from "@localess/react";

const Hero = ({ data }: { data: HeroBlock }) => (
  <section {...localessEditable(data)}>
    <h1 {...localessEditableField<HeroBlock>('title')}>{data.title}</h1>
    <p {...localessEditableField<HeroBlock>('subtitle')}>{data.subtitle}</p>
  </section>
);

App Router with React Server Components

Use @localess/react/rsc when you want React Server Components and Visual Editor live editing together.

Initialize as in Setup, in a module your pages import.

Rendering — app/[locale]/page.tsx

Use LocalessDocument for a zero-boilerplate live-editing integration, or useLocaless for client-side re-fetching with more control.

Option A — LocalessDocument (recommended): it's a Server Component internally (only its sync subscription runs client-side), so it renders directly in the Server Component — no separate Client Component file needed.

import { getLocalessClient, LocalessDocument } from "@localess/react/rsc";

export default async function Home({ params }: { params: Promise<{ locale: string }> }) {
  const { locale } = await params;
  const content = await getLocalessClient().getContentBySlug("home", { locale });
  return <LocalessDocument document={content} />;
}

Option B — useLocaless hook: re-fetches on the client, so it needs an actual 'use client' file.

// app/[locale]/page.tsx (Server Component)
import { getLocalessClient } from "@localess/react/rsc";
import PageClient from "./page-client";

export default async function Home({ params }: { params: Promise<{ locale: string }> }) {
  const { locale } = await params;
  const content = await getLocalessClient().getContentBySlug("home", { locale });
  return <PageClient initialContent={content} locale={locale} />;
}
// app/[locale]/page-client.tsx (Client Component)
'use client';
import { useLocaless, LocalessComponent, localessEditable } from "@localess/react/rsc";

export default function PageClient({ initialContent, locale }) {
  const content = useLocaless("home", { locale }) ?? initialContent;
  return (
    <main {...localessEditable(content.data)}>
      {content.data?.body?.map(item => (
        <LocalessComponent key={item._id} data={item} links={content.links} references={content.references} />
      ))}
    </main>
  );
}

useLocaless runs in the browser, where the server-side localessInit() never ran. Call localessInit a second time inside the Client Component boundary, with a public token, exactly as in the static export fallback — or prefer Option A.

Playground: playgrounds/next is a full working Next.js App Router project built on Option A — localessInit() in src/shared/utils/locales.ts, imported by the page, and <LocalessDocument document={document} /> rendered directly from the Server Component.

Static Export (output: 'export')

Use @localess/react/ssr when your Next.js project uses output: 'export' for static site generation. The /rsc live editing isn't available here — for live editing on a static build, see the static export fallback.

next.config.js

/** @type {import('next').NextConfig} */
module.exports = { output: 'export' };

Setup — lib/localess.ts

import { localessInit } from "@localess/react/ssr";
import { Page, Header, Teaser } from "@/components";

export const localessClient = localessInit({
  origin: process.env.LOCALESS_ORIGIN!,
  spaceId: process.env.LOCALESS_SPACE_ID!,
  token: process.env.LOCALESS_TOKEN!,
  // enableSync is not applicable in static export — omit or set to false
  components: { Page, Header, Teaser },
});

Page — app/page.tsx

import { LocalessServerComponent } from "@localess/react/ssr";
import { getLocalessClient } from "@localess/react/ssr";
import "@/lib/localess"; // ensure init runs

export default async function Home() {
  const client = getLocalessClient();
  const content = await client.getContentBySlug("home", { locale: "en" });
  return (
    <main>
      <LocalessServerComponent data={content.data} links={content.links} references={content.references} />
    </main>
  );
}

@localess/react/ssr also exports LocalessServerDocument, which takes the full Content<T> response as a single document prop (like LocalessDocument, but with no sync attributes since live editing has no meaning once the HTML is pre-baked):

import { LocalessServerDocument } from "@localess/react/ssr";

<LocalessServerDocument document={content} />

Playground: playgrounds/next-static mirrors the RSC playground but targets output: 'export' and uses @localess/react/ssr end to end.

Playground: playgrounds/next (App Router + RSC) and playgrounds/next-static (output: 'export') are the reference implementations for this guide.

AI Coding Agents

This package ships a SKILL.md file that provides AI coding agents (GitHub Copilot, Claude Code, Cursor, and others) with accurate, up-to-date APIs, patterns, and best practices.

Reference it from your project's AGENTS.md:

## Localess

@node_modules/@localess/react/SKILL.md

License

MIT

On this page