Getting Started
Angular SDK for Localess — content delivery, rich text rendering, asset management, dynamic component rendering, and Visual Editor integration for client-side and server-side rendered Angular applications.
@localess/angular ships as a single unified package — there is no /browser or /server subpath split. Register provideLocaless() once, in app.config.ts; the same providers are used for both server-side rendering and the browser.
Security:
provideLocaless()takes a singletoken, bundled into the browser JS. Only a public token (read-only, published content and translations only) should ever be used here — never a secret token. What makes SSR apps safe and efficient isLocalessContentService— it fetches once on the server and hydrates the result to the browser via Angular'sTransferState, so the browser normally never makes its own API call at all. See SSR and content fetching.
Requirements
- Node.js >= 24.0.0
@angular/core,@angular/common,@angular/compiler,@angular/platform-browser— versions>=21.0.0 <23.0.0
Installation
# npm
npm install @localess/angular@latest
# yarn
yarn add @localess/angular@latest
# pnpm
pnpm add @localess/angular@latestQuick Start
1. Register the provider in app.config.ts, with a public (read-only) token — this configuration is bundled into the browser. withLocalessComponents() registers the map of content _schema keys to Angular components used for dynamic rendering:
import { provideLocaless, withLocalessComponents } from '@localess/angular';
import { PageComponent } from './shared/components/localess/page/page.component';
export const appConfig: ApplicationConfig = {
providers: [
provideLocaless(
{
origin: 'https://my-localess.web.app',
spaceId: 'YOUR_SPACE_ID',
token: 'YOUR_PUBLIC_TOKEN',
enableSync: true,
},
withLocalessComponents({
Page: PageComponent, // eager — always needed, it's the page root
Button: () => import('./shared/components/localess/button/button.component').then(m => m.ButtonComponent), // lazy
})
),
],
};2. For SSR apps, app.config.server.ts only needs provideServerRendering() — the single provideLocaless() call from app.config.ts already applies on the server, no separate registration needed:
import { provideServerRendering, withRoutes } from '@angular/ssr';
import { ApplicationConfig, mergeApplicationConfig } from '@angular/core';
import { appConfig } from './app.config';
import { serverRoutes } from './app.routes.server';
const serverConfig: ApplicationConfig = {
providers: [provideServerRendering(withRoutes(serverRoutes))],
};
export const config = mergeApplicationConfig(appConfig, serverConfig);3. Fetch content with LocalessContentService, e.g. in a route resolver:
import { inject } from '@angular/core';
import { ResolveFn, Routes } from '@angular/router';
import { Content, LocalessApiError, LocalessContentService } from '@localess/angular';
import { SlugComponent } from './slug/slug.component';
const contentResolver: ResolveFn<Content | undefined> = async route => {
try {
return await inject(LocalessContentService).contentBySlug(route.url.map(s => s.path).join('/'));
} catch (error) {
if (error instanceof LocalessApiError && error.status === 404) {
return undefined;
}
throw error;
}
};
export const routes: Routes = [
{
path: '**',
component: SlugComponent,
resolve: { content: contentResolver },
},
];On the server, the fetched content is written to TransferState; on the browser, the same call reads it back out instead of re-fetching (or, in a pure client-side-rendered app with no SSR, falls back to fetching directly using the public token).
4. Render it with <ll-document>, which dynamically resolves and mounts the registered component for the content's _schema:
import { Component, input } from '@angular/core';
import { Content, LocalessDocument } from '@localess/angular';
import { Page } from './shared/models/localess';
@Component({
selector: 'app-slug',
imports: [LocalessDocument],
templateUrl: './slug.component.html',
})
export class SlugComponent {
content = input<Content<Page>>();
}<!-- slug.component.html -->
@if (content(); as content) {
<ll-document [document]="content" />
}Playground:
playgrounds/angular-ssris a full working Angular SSR project wired up end-to-end withprovideLocaless,withLocalessComponents, and<ll-document>.
Setup
provideLocaless(options, ...features) registers everything: LocalessClientService, LocalessContentService, LocalessAssetService, LocalessTranslationService, LocalessSyncService, LocalessComponentResolver, and Angular's IMAGE_LOADER.
import { provideLocaless } from '@localess/angular';
provideLocaless({
origin: 'https://my-localess.web.app', // Required. Localess instance URL (no trailing slash)
spaceId: 'YOUR_SPACE_ID', // Required. Found in Localess Space settings
token: 'YOUR_PUBLIC_TOKEN', // Required. Always public — this config is bundled into the browser
version: 'draft', // Optional. Omit for published content
enableSync: true, // Optional. Loads the Visual Editor sync script
cacheTTL: 300, // Optional. Seconds; false disables caching
debug: false, // Optional. Enables console logging
})| Option | Type | Required | Description |
|---|---|---|---|
origin | string | ✅ | Fully qualified Localess URL, e.g. https://my-localess.web.app |
spaceId | string | ✅ | Space ID from the Localess Space settings |
token | string | ✅ | Always a public token — this config is bundled into the browser JS |
version | 'draft' | — | Fetch draft content; omit for published |
enableSync | boolean | — | When true, injects the Visual Editor sync script into the page |
cacheTTL | number | false | — | Response cache TTL in seconds (default 300); false disables caching |
debug | boolean | — | When true, logs internal activity to the console |
provideLocaless() also registers Angular's built-in IMAGE_LOADER provider so that NgOptimizedImage automatically appends ?w=<width> to Localess asset URLs for responsive image optimization. See Angular Image Optimization.
withLocalessComponents(components, fallback?)
Registers the component registry consumed by [llComponent] and <ll-document> to dynamically render content by its _schema key. Pass it as a trailing argument to provideLocaless():
import { provideLocaless, withLocalessComponents } from '@localess/angular';
provideLocaless(
{ origin: '...', spaceId: '...', token: '...' },
withLocalessComponents(
{
Hero: HeroComponent, // eager — direct component reference
Teaser: () => import('./teaser.component').then(m => m.TeaserComponent), // lazy
},
UnknownBlockComponent // optional fallback, rendered when a _schema key has no match
)
);Schema keys must match the _schema value from the CMS exactly. A lazy loader is only invoked once per schema key — resolved components are cached by LocalessComponentResolver.
Each registered value should be a component that extends SchemaComponent<T>, rather than an arbitrary component with its own data/links/references/assets inputs. [llComponent] calls ComponentRef.setInput() for these names when the target component declares them — extending SchemaComponent<T> guarantees it does, with data typed and required, so a mismatched or missing schema type surfaces as a compile error instead of a silently-undefined input at runtime.
Schema Components
SchemaComponent<T> is the abstract base class you extend to render a Localess content schema. It automatically sets the data-ll-id and data-ll-schema attributes on the host element so the Localess Visual Editor can highlight and select components on the page.
The base class declares four signal inputs:
| Input | Type | Description |
|---|---|---|
data | input.required<T>() | The schema data object (required) |
links | input<Links>() | Map of content ID → slug, used by findLink() |
references | input<References>() | Map of resolved ContentReference objects |
assets | input<Assets>() | Map of asset metadata |
import { Component } from '@angular/core';
import { SchemaComponent } from '@localess/angular';
@Component({
selector: 'app-schema-hero-section',
standalone: true,
templateUrl: './hero-section.component.html',
})
export class HeroSectionComponent extends SchemaComponent<HeroSection> {}In the template, read inputs with function-call syntax and use the assetUrl() and findLink() helpers provided by the base class:
<section>
<h1>{{ data().title }}</h1>
<img [src]="assetUrl(data().backgroundImage)" [alt]="data().title" />
<a [href]="findLink(data().ctaLink)">Learn more</a>
</section>A SchemaComponent is typically mounted automatically by [llComponent]/<ll-document> via the component registry — you generally don't pass its inputs manually, but you can:
<app-schema-hero-section [data]="content.data" [links]="content.links" [references]="content.references" [assets]="content.assets" />Base class helpers
| Member | Signature | Description |
|---|---|---|
assetUrl(asset, params?) | (asset: ContentAsset, params?: AssetTransformParams) => string | Builds the full CDN URL for a Localess asset, with optional transform params |
findLink(link) | (link: ContentLink) => string | Resolves a CMS link to a path or URL, using the links input |
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/angular/SKILL.md