Localess
Angular

Content

Fetch content with TransferState hydration and render it dynamically by schema with the Angular SDK for Localess.

SSR and content fetching

Always fetch content through LocalessContentService, not LocalessClientService directly, in an SSR app. It's what makes SSR both correct and efficient:

  1. On the server, it calls the API and writes the result into Angular's TransferState.
  2. On the browser, for the same slug/ID/params it reads the value straight out of TransferState instead of making a second network call — so the app never double-fetches on hydration.
  3. Only if there's no matching TransferState entry (pure client-side navigation after the first render, or a fully CSR app) does the browser call the API directly — which is exactly why the token registered in app.config.ts must be a public, read-only one.

This is handled by one provideLocaless() registration — you don't need a second one in app.config.server.ts for this to work.

Content Service

LocalessContentService fetches content and hydrates it from server to browser via TransferState (see SSR and content fetching above). All methods return a Promise — call from anywhere async/await works, e.g. a route resolver or an event handler; wrap in Angular's resource() yourself if you need reactive re-fetching.

import { LocalessContentService } from '@localess/angular';
import { inject } from '@angular/core';

const contentService = inject(LocalessContentService);

contentBySlug<T>(slug, params?)

content = await contentService.contentBySlug<HeroSection>('home');

// With params
content = await contentService.contentBySlug<HeroSection>('home', {
  version: 'draft',
  locale: 'en',
  resolveReference: true,
  resolveLink: true,
});

contentById<T>(id, params?)

content = await contentService.contentById<ArticlePage>('abc123', { locale: 'fr' });

links(params?)

links = await contentService.links({ kind: 'DOCUMENT', parentSlug: 'blog', excludeChildren: false });

ContentFetchParams

ParameterTypeDescription
version'draft'Override the global version for this request
localestringLocale code, e.g. 'en', 'fr'
resolveReferencebooleanInline referenced content objects
resolveLinkbooleanInline link objects
resolveAssetbooleanInline referenced asset metadata

LinksFetchParams

ParameterTypeDescription
kindstringFilter links by content kind
parentSlugstringReturn only links under this parent slug
excludeChildrenbooleanExclude descendant slugs

Reading the result

Each method returns a Promise that rejects on a failed fetch (e.g. LocalessApiError for a 404) — handle it wherever you call it, as shown in the Quick Start resolver above.

Dynamic Component Rendering

Two building blocks render content by its _schema key using the registry configured with withLocalessComponents().

<ll-document>

Renders a full Content response and keeps it in sync with the Localess Visual Editor. Wraps [llComponent] with a signal seeded from document().data, and subscribes once to LocalessSyncService.onChange() so input/change events replace the rendered content live — no manual sync wiring needed.

import { LocalessDocument } from '@localess/angular';

@Component({
  imports: [LocalessDocument],
  template: `<ll-document [document]="content()" />`,
})
export class SlugComponent {
  content = input.required<Content<Page>>();
}

Sync only activates when LocalessSyncService.enabled() is true (enableSync: true was passed to provideLocaless, running in the browser, inside the Visual Editor iframe).

[llComponent] directive

Dynamically renders the component registered for a given content object's _schema key. <ll-document> uses this directive internally — use it directly to render nested/sub-schema content, such as a list of blocks:

<ng-container [llComponent]="content.data" [links]="content.links" [references]="content.references" [assets]="content.assets" />

@for (button of data().buttons; track button._id) {
  <ng-container [llComponent]="button" [links]="links()" [references]="references()" [assets]="assets()" />
}

It renders directly at its ng-container anchor — no wrapper element — so for a list, apply it inside an @for block rather than passing an array. The directive recreates the component only when _schema changes; otherwise it reuses the existing instance and updates its data/links/references/assets inputs (only the inputs the target component actually declares), so unrelated content edits don't tear down component state.

import { LocalessComponentDirective } from '@localess/angular';

@Component({ imports: [LocalessComponentDirective] })
export class PageComponent extends SchemaComponent<Page> {}

On this page