---
title: @storyblok/angular
description: @storyblok/angular is Storyblok's official SDK for Angular applications.
url: https://www.storyblok.com/docs/libraries/js/angular-sdk
---

# @storyblok/angular

[@storyblok/angular](https://github.com/storyblok/monoblok/tree/main/packages/angular) is Storyblok’s official SDK for Angular applications.

## Requirements

-   **Angular** version 19 or later
-   **Node.js** LTS (version 22.x recommended)
-   **Modern web browser** (for example, Chrome, Firefox, Safari, Edge — latest versions)

## Installation

Add the package to a project by running this command in the terminal:

```bash
npm install @storyblok/angular@latest
```

## Usage

### Configuration

Import and initialize the SDK using the access token of a Storyblok space.

src/app/app.config.ts

```ts
import { ApplicationConfig, provideBrowserGlobalErrorListeners } from "@angular/core";
import { provideRouter, withComponentInputBinding } from "@angular/router";
import { routes } from "./app.routes";
import { provideClientHydration, withEventReplay } from "@angular/platform-browser";
import { provideStoryblok, withLivePreview, withStoryblokComponents, type StoryblokClientConfig } from "@storyblok/angular";

const sbConfig: StoryblokClientConfig = {
  accessToken: "YOUR_ACCESS_TOKEN",
  region: "eu",
  inlineRelations: true,
};
export const appConfig: ApplicationConfig = {
  providers: [
    provideBrowserGlobalErrorListeners(),
    provideRouter(routes, withComponentInputBinding()),
    provideClientHydration(withEventReplay()),
    provideStoryblok(
      sbConfig,
      withStoryblokComponents({
        page: () => import("./components/page/page.component").then((m) => m.PageComponent),
      }),
      withLivePreview()
    ),
  ],
};
```

> [!TIP]
> Learn how to retrieve an access token in the [access tokens concept](/docs/concepts/access-tokens).

> [!WARNING]
> The `region` parameter must be specified unless the space was created in the EU. Learn more in the [@storyblok/js reference](/docs/libraries/js/js-sdk).

### Components

Create an Angular component for each block defined in Storyblok and registered in the configuration. Each component will receive a `blok` prop, containing the content of the block.

src/app/components/feature/feature.component.ts

```ts
import { Component, ChangeDetectionStrategy, input } from "@angular/core";
export interface FeatureBlok {
  headline?: string;
}
@Component({
  selector: "app-feature",
  changeDetection: ChangeDetectionStrategy.OnPush,
  template: `<h3>{{ blok().headline }}</h3>`,
})
export class FeatureComponent {
  readonly blok = input.required<FeatureBlok>();
}
```

Use `<sb-component>` to automatically render nested components (provided they are registered globally).

src/app/components/page/page.component.ts

```ts
import { Component, ChangeDetectionStrategy, input, computed } from "@angular/core";
import { type SbBlokData, StoryblokComponent } from "@storyblok/angular";

export interface PageBlok {
  body?: SbBlokData[];
}

@Component({
  selector: "app-page",
  standalone: true,
  changeDetection: ChangeDetectionStrategy.OnPush,
  imports: [StoryblokComponent],
  template: `
    <div>
      <sb-component [sbBlok]="bloks()" />
    </div>
  `,
})
export class PageComponent {
  readonly blok = input.required<PageBlok>();
  readonly bloks = computed(() => this.blok().body ?? []);
}
```

### Fetching and rendering

Use the client to fetch a story and render the content using `StoryblokComponent`.

src/app/routes/home/home.component.ts

```ts
import { Component, ChangeDetectionStrategy, inject, signal, computed, OnInit } from "@angular/core";
import { StoryblokComponent, StoryblokService, Story } from "@storyblok/angular";

@Component({
  selector: "app-home",
  changeDetection: ChangeDetectionStrategy.OnPush,
  imports: [StoryblokComponent],
  template: `
    <div>
      <!-- Pass content directly - component handles null internally -->
      <sb-component [sbBlok]="storyContent()" />
    </div>
  `,
})
export class HomeComponent implements OnInit {
  private readonly storyblok = inject(StoryblokService);
  private client = this.storyblok.getClient();
  readonly story = signal<Story | null>(null);
  readonly loading = signal(true);
  readonly storyContent = computed(() => this.story()?.content);

  async ngOnInit(): Promise<void> {
    try {
      const { data } = await this.client.stories.get("home", {
        query: {
          version: "draft",
        },
      });
      this.story.set((data?.story as Story) || null);
    } catch (error) {
      throw error;
    } finally {
      this.loading.set(false);
    }
  }
}
```

## API

### `provideStoryblok`

Import and initialize the SDK to access and configure all features.

```ts
import { provideStoryblok } from "@storyblok/angular";

provideStoryblok(OPTIONS);
```

`provideStoryblok()` creates an instance of the Storyblok Content Delivery API (CAPI) client and loads the Storyblok Bridge.

All The following options are available:

| Key | Description | Type |
| --- | --- | --- |
| `config` | All options listed in the [@storyblok/api-client package reference](https://github.com/storyblok/monoblok/tree/main/packages/capi-client) are available in the first parameter. | `StoryblokClientConfig` |
| `withStoryblokComponents` | An object that maps Angular components to Storyblok blocks. Each component receives a `blok` input containing the content of the block. | `object` |
| `withLivePreview` | Enable or disable live preview for the Angular application. | `BridgeParams \| undefined` |
| `withStoryblokRichtextComponents` | An object that maps Angular components to Storyblok richtext mark and nodes. Each component receives a `data` input containing the content of the richtext. | `object` |

#### Example: `withStoryblokComponents`

Register Angular components as follows. The key represents the technical name of the Storyblok block, the value represents the Angular component. It supports both lazy and eager loading.

```ts
import { type StoryblokComponentsMap } from "@storyblok/angular";
import { PageComponent } from "./components/page/page.component";
const storyblokComponents: StoryblokComponentsMap = {
  page: PageComponent,
  teaser: () => import("./components/teaser/teaser.component").then((m) => m.TeaserComponent),
  grid: () => import("./components/grid/grid.component").then((m) => m.GridComponent),
  feature: () => import("./components/feature/feature.component").then((m) => m.FeatureComponent),
  "featured-articles": () => import("./components/feature-posts/feature-posts.component").then((m) => m.FeaturePostsComponent),
  "article-overview": () => import("./components/article-overview/article-overview.component").then((m) => m.ArticleOverviewComponent),
  article: () => import("./components/article/article.component").then((m) => m.ArticleComponent),
};
provideStoryblok(withStoryblokComponents(storyblokComponents));
```

#### Example: `withLivePreview`

`withLivePreview` enables and disables live preview for the Angular application. Pass `BridgeParams` to configure the Storyblok Bridge globally.

```ts
withLivePreview({
  resolveRelations: ['feature_posts.posts'],
}),
```

#### Example: `withStoryblokRichtextComponents`

Register custom Angular components for richtext nodes and marks.

The key is the technical name of the Storyblok node or mark, and the value is the Angular component used to render it. Both eager and lazy loaded components are supported, with full type safety for keys and component props.

```ts
withStoryblokRichtextComponents({
  link: () => import("./components/richtext/link.component").then((m) => m.LinkComponent),
  heading: () => import("./components/richtext/heading.component").then((m) => m.HeadingComponent),
});
```

See the [rich text rendering](#sb-rich-text) for further details on custom rich text components.

### `StoryblokService`

`StoryblokService` is the main entry point to access the underlying Storyblok `@storyblok/api-client`. It provides a typed, framework-aware wrapper with full access to the official Storyblok API client.

Use `getClient()` when you need direct access to the Storyblok SDK methods (for example fetching stories, datasources, or links).

```ts
const client = inject(StoryblokService).getClient();

const { data } = await client.stories.get(path, {
  query: {
    version: "draft",
    resolve_relations: "featured-articles.articles",
  },
});
```

See the [@storyblok/api-client reference](https://github.com/storyblok/monoblok/tree/main/packages/capi-client).

### `LivePreviewService`

`LivePreviewService` enables real-time updates from the Storyblok Visual Editor. It listens for story changes and automatically updates your local state when editing in preview mode.

Use it alongside the Storyblok client to fetch the initial story, then subscribe to live updates.

```ts
export class ExampleComponent {
  private readonly livePreview = inject(LivePreviewService);

  private readonly story = signal<Story | null>(null);
  readonly bridgeConfig: BridgeParams = {
    resolveRelations: ["featured-articles.articles"],
  };

  ngOnInit(): void {
    this.livePreview.listen((updatedStory) => {
      this.story.set((updatedStory as Story) || null);
    }, this.bridgeConfig);
  }
}
```

This keeps responsibilities clean: `StoryblokService` handles data fetching, while `LivePreviewService` manages real-time synchronization.

### `<sb-component>`

`<sb-component>` renders Storyblok blocks dynamically. The `sbBlok` input accepts either a single Storyblok block or an array of blocks.

```html
<sb-component [sbBlok]="blok.body" />
```

### `SbBlokDirective`

`SbBlokDirective` renders a single Storyblok block dynamically inside an Angular template. It accepts a `sbBlok` input.

```html
<ng-container [sbBlok]="blok" />
```

### `<sb-rich-text>`

Used to render a rich text field from a Storyblok story.

```html
<sb-rich-text [sbDocument]="blok.richtext_field" />
```

The component accepts the following optional inputs:

-   `optimizeImage`: Allows image optimization options to be configured for image nodes.

> [!TIP]
> When using TypeScript, custom components are fully type-safe through the `SbAngularRichTextProps<T>` utility type. For example, `SbAngularRichTextProps<"link">` provides access to the attributes available for the `link` mark, while `SbAngularRichTextProps<"heading">` provides access to heading-specific attributes such as `level`.

See the [@storyblok/richtext reference](/docs/libraries/js/rich-text) for a complete list of supported rich text nodes, marks, and customization options.

#### Custom components

Custom components can be registered using `withStoryblokRichtextComponents` when configuring `provideStoryblok`. The key must match a supported rich text node or mark name.

```ts
import type { SbAngularRichTextComponentMap } from "@storyblok/angular";
import { withStoryblokRichtextComponents } from "@storyblok/angular";

import CustomHeading from "./CustomHeading.component";
import CustomLink from "./CustomLink.component";
import CustomTable from "./CustomTable.component";

const components: SbAngularRichTextComponentMap = {
  heading: CustomHeading,
  link: CustomLink,
  table: CustomTable,
};

provideStoryblok(withStoryblokRichtextComponents(components));
```

Each component receives a required `data` input with the richtext node or mark data.

#### Example: Custom link component (mark)

Marks receive their attributes through `SbAngularRichTextProps<T>` and can render their content using `<ng-content>`.

```ts
import { ChangeDetectionStrategy, Component, input } from "@angular/core";
import type { SbAngularRichTextProps } from "@storyblok/angular";

@Component({
  selector: "app-link",
  changeDetection: ChangeDetectionStrategy.OnPush,
  template: `
    <a [href]="data().attrs?.href">
      <ng-content />
    </a>
  `,
  host: {
    style: "display: inline-block",
  },
})
export class LinkComponent {
  readonly data = input.required<SbAngularRichTextProps<"link">>();
}
```

#### Example: Custom heading component (node)

Nodes receive their child nodes through `content`. These can be recursively rendered using `<sb-rich-text>`.

```ts
import { ChangeDetectionStrategy, Component, computed, input } from "@angular/core";
import { SbRichTextComponent, type SbAngularRichTextProps } from "@storyblok/angular";

@Component({
  selector: "app-heading",
  changeDetection: ChangeDetectionStrategy.OnPush,
  imports: [SbRichTextComponent],
  template: `
    @switch (level()) {
      @case (1) {
        <h1>
          <sb-rich-text [sbDocument]="data().content" />
        </h1>
      }
      @default {
        <div>
          <sb-rich-text [sbDocument]="data().content" />
        </div>
      }
    }
  `,
  host: {
    style: "display: contents",
  },
})
export class HeadingComponent {
  readonly data = input.required<SbAngularRichTextProps<"heading">>();

  readonly level = computed(() => this.data().attrs?.level ?? 1);
}
```

#### Example: Custom table component (advanced node)

For advanced use cases, nodes can use the `splitTableRows` utility to separate table rows into header and body sections.

```ts
import { ChangeDetectionStrategy, Component, computed, input } from "@angular/core";
import { SbRichTextComponent, splitTableRows, type SbAngularRichTextProps } from "@storyblok/angular";

@Component({
  selector: "app-table",
  changeDetection: ChangeDetectionStrategy.OnPush,
  imports: [SbRichTextComponent],
  template: `
    <table>
      @if (tableRows().headerRows.length) {
        <thead>
          <sb-rich-text [sbDocument]="tableRows().headerRows" />
        </thead>
      }
      @if (tableRows().bodyRows.length) {
        <tbody>
          <sb-rich-text [sbDocument]="tableRows().bodyRows" />
        </tbody>
      }
    </table>
  `,
  host: {
    style: "display: contents",
  },
})
export class TableComponent {
  readonly data = input.required<SbAngularRichTextProps<"table">>();

  readonly tableRows = computed(() => splitTableRows(this.data().content));
}
```

### Utility helpers

The Angular SDK also exports the core rich text utilities from `@storyblok/richtext`. These can be useful when implementing advanced custom components, such as custom image or table rendering.

Available utilities include:

-   `renderRichText`: The core rich text rendering function.
-   `buildStoryblokImage`: Generates optimized Storyblok Image Service URLs.
-   `splitTableRows`: Splits table rows into `headerRows` and `bodyRows`.

## Further resources

[Repository Playground](https://github.com/storyblok/monoblok/tree/main/packages/angular/playground)See the repository playground for additional examples.

[Angular Guide](/docs/quickstarts/angular)See the Angular guide for a comprehensive walkthrough on integrating Storyblok with Angular.

[Space Blueprint: Angular](https://github.com/storyblok/blueprint-core-angular)See the core space blueprint for Angular to kickstart a new project.

## Pagination

-   [Previous: Svelte SDK](/docs/libraries/js/svelte-sdk)
-   [Next: Preview Bridge](/docs/libraries/js/preview-bridge)
