The @localess/react package is the official React integration for the Localess headless CMS platform. In a React Router v7 framework-mode app you wire it up through @localess/react/vite, which generates the localessInit() call for the SSR graph and the browser bundle alike — so the same setup serves both server-rendered and prerendered (prerender) output.
⚠️ Security Notice:@localess/react/vite ships token to the browser bundle as well as the SSR graph — there is currently no secret/public split for this plugin. Treat it as a public, read-only value.
For Vite-based SSR frameworks (TanStack Start, React Router v7 framework mode), @localess/react/vite replaces a manual localessInit() call with one Vite plugin, generating the same localessInit() call identically for every build graph (SSR and browser):
// vite.config.tsimport { defineConfig } from 'vite';import { localess } from '@localess/react/vite';export default defineConfig({ plugins: [ localess({ origin: process.env.LOCALESS_ORIGIN!, spaceId: process.env.LOCALESS_SPACE_ID!, token: process.env.LOCALESS_TOKEN!, enableSync: true, // Components are auto-discovered from this directory — no manual registry. componentsDir: 'src/components/localess', // default: 'src' // Files are lowercase (page.tsx) while the schemas are PascalCase (Page), // so a case-insensitive naming strategy reconciles the two. componentNaming: 'camelCase', // default: 'exact' components: { 'HeroSection': './HeroOverride.tsx#HeroOverride' }, // optional, overrides auto-discovery }), ],});
Every .tsx/.jsx file under componentsDir is auto-registered under its filename verbatim — page.tsx registers as page — and that key is matched against data._schema.
Because React filenames and CMS schema names rarely share a convention, set componentNaming to normalize both sides before they're compared:
Strategy
HeroBanner / hero-banner / hero_banner resolve as
exact(default)
unchanged — only an identical spelling matches
camelCase
heroBanner
PascalCase
HeroBanner
kebab-case
hero-banner
snake_case
hero_banner
lowercase
herobanner — separators dropped entirely
Every strategy except exact is case- and separator-insensitive, so they differ only in the key shape they produce, not in what they match. Both playgrounds use camelCase for exactly the reason in the comment above: lowercase files, PascalCase schemas.
componentNaming applies to auto-discovery only. components overrides take an exact key and a file path relative to componentsDir; a bare path assumes a default export, suffix it with #ExportName to import a named export instead.
TypeScript: if your tsconfig sets noUncheckedSideEffectImports: true, the bare import 'virtual:localess-init' below fails to resolve under tsc unless you add the plugin's ambient module declarations to types:
Then import the virtual module wherever you fetch. The bare import 'virtual:localess-init' is what runs the generated localessInit() call, so it must appear in the module doing the fetching — not only once at the app root:
Known gap: unlike every other export, token here is shipped to the browser bundle as well as the SSR graph — there is currently no secret/public token split for this plugin. Treat token as a public value when using localess() from @localess/react/vite, until a scoped/public-token mechanism replaces this.
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.
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.