Localess
Angular

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 single token, 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 is LocalessContentService — it fetches once on the server and hydrates the result to the browser via Angular's TransferState, 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@latest

Quick 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-ssr is a full working Angular SSR project wired up end-to-end with provideLocaless, 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
})
OptionTypeRequiredDescription
originstringFully qualified Localess URL, e.g. https://my-localess.web.app
spaceIdstringSpace ID from the Localess Space settings
tokenstringAlways a public token — this config is bundled into the browser JS
version'draft'Fetch draft content; omit for published
enableSyncbooleanWhen true, injects the Visual Editor sync script into the page
cacheTTLnumber | falseResponse cache TTL in seconds (default 300); false disables caching
debugbooleanWhen 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:

InputTypeDescription
datainput.required<T>()The schema data object (required)
linksinput<Links>()Map of content ID → slug, used by findLink()
referencesinput<References>()Map of resolved ContentReference objects
assetsinput<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

MemberSignatureDescription
assetUrl(asset, params?)(asset: ContentAsset, params?: AssetTransformParams) => stringBuilds the full CDN URL for a Localess asset, with optional transform params
findLink(link)(link: ContentLink) => stringResolves 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

License

MIT

On this page