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:
- On the server, it calls the API and writes the result into Angular's
TransferState. - On the browser, for the same slug/ID/params it reads the value straight out of
TransferStateinstead of making a second network call — so the app never double-fetches on hydration. - Only if there's no matching
TransferStateentry (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 inapp.config.tsmust 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
| Parameter | Type | Description |
|---|---|---|
version | 'draft' | Override the global version for this request |
locale | string | Locale code, e.g. 'en', 'fr' |
resolveReference | boolean | Inline referenced content objects |
resolveLink | boolean | Inline link objects |
resolveAsset | boolean | Inline referenced asset metadata |
LinksFetchParams
| Parameter | Type | Description |
|---|---|---|
kind | string | Filter links by content kind |
parentSlug | string | Return only links under this parent slug |
excludeChildren | boolean | Exclude 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> {}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.
Visual Editor
Subscribe to Localess Visual Editor live-editing events with the Angular SDK for Localess.