---
title: @storyblok/angular
description: @storyblok/angular is Storyblok's official SDK for Angular applications.
url: https://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 - componnent handles null internally <!-- Pass content directly - componnent 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),
});
```

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

##### Example: Custom link component

src/app/components/richtext/link.component.ts

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

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

##### Example: Custom heading component

src/app/components/richtext/heading.component.ts

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

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

### `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>`

`<sb-rich-text>` renders Storyblok rich text documents. It accepts a `sbDocument` input, which represents the rich text JSON structure.

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

## Further resources

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

[Angular Guide](/docs/guides/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)
