---
title: Integrate Angular with Storyblok
description: Discover Storyblok's documentation with comprehensive developer guides, user manuals, API references, and examples to help you get the most out of the headless CMS platform.
url: https://storyblok.com/docs/guides/angular
---

# Integrate Angular with Storyblok

Use Storyblok to manage the content of your Angular application.

> [!NOTE]
> This guide has been tested with the following package versions:
> 
> -   `@angular/core@21.2.0`
> -   `storyblok-angular@0.2.1`
> -   Node.js v22.13.0

## Setup

In the terminal, install the Angular CLI.

```bash
npm install -g @angular/cli
```

Create a new Angular project with SSR support, following the [official installation](https://angular.dev/installation#create-a-new-project) page.

```bash
ng new my-storyblok-app --ssr
cd my-storyblok-app
```

Create a [new blank space](https://app.storyblok.com/#/me/spaces/new?tab=select-plan) to follow the tutorial from scratch, or start from the [core blueprint](https://github.com/storyblok/blueprint-core-angular).

[No Storyblok account yet?](https://app.storyblok.com/#/signup) Create one and start a free Storyblok space

## Installation

In the terminal, install the `@storyblok/angular` package.

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

To use environment variables in your Angular app, [generate environments](https://angular.dev/cli/generate/environments) files with the Angular CLI.

```bash
ng generate environments
```

Add the Storyblok access token to the environment files in the `src/environments` folder. Set `production` to `false` in `environment.ts` file and to `true` in `environment.production.ts` file.

src/environments/environment.ts

```typescript
export const environment = {
  production: true,
  accessToken: 'YOUR-ACCESS-TOKEN'
};
```

src/environments/environment.development.ts

```typescript
export const environment = {
  production: false,
  accessToken: 'YOUR-ACCESS-TOKEN'
};
```

> [!NOTE]
> Learn how to get an [access token](/docs/concepts/access-tokens) for your Storyblok project.

> [!NOTE]
> Angular automatically selects the appropriate environment file based on the build configuration. For more information on using environments, refer to the [official Angular documentation](https://angular.dev/tools/cli/environments#using-environment-specific-variables-in-your-app).

Update the `app.config.ts` file to configure the Storyblok Angular package.

src/app/app.config.ts

```typescript
import { ApplicationConfig, provideBrowserGlobalErrorListeners } from '@angular/core';
import { provideRouter } from '@angular/router';

import { routes } from './app.routes';
import { provideClientHydration, withEventReplay } from '@angular/platform-browser';
 import { environment } from '../environments/environment';
 import { provideStoryblok } from '@storyblok/angular';

export const appConfig: ApplicationConfig = {
  providers: [
    provideBrowserGlobalErrorListeners(),
    provideRouter(routes),
    provideClientHydration(withEventReplay()),
    provideStoryblok({
      accessToken: environment.accessToken,
      region: 'eu',
 }),
  ],
};
```

The Storyblok Angular package provides features such as fetching content from the Content Delivery API, component registration, and real-time visual editing available across your project.

## Fetch a single story

Create a new file `src/app/routes/home.component.ts` and add the following code.

src/app/routes/home.component.ts

```typescript
import { Component, ChangeDetectionStrategy, inject, signal, OnInit } from '@angular/core';
import { StoryblokService, StoryblokComponent, type SbBlokData } from '@storyblok/angular';

@Component({
  selector: 'app-home',
  changeDetection: ChangeDetectionStrategy.OnPush,
  imports: [StoryblokComponent],
  template: `
    <div>
      <sb-component [sbBlok]="storyContent()" />
    </div>
  `,
})
export class HomeComponent implements OnInit {
  private readonly storyblok = inject(StoryblokService);
  readonly storyContent = signal<SbBlokData | null>(null);

  async ngOnInit() {
    const client = this.storyblok.getClient();
    const { data } = await client.stories.get('home', {
      query: { version: 'draft' },
    });
    this.storyContent.set(data?.story?.content ?? null);
  }
}
```

`StoryblokService` provides access to the Storyblok API client to fetch story data. `StoryblokComponent` dynamically renders content type and nestable blocks. In this case, it looks for the content type block of the home story.

## Create and register blocks

Create `page.component.ts` component to render all stories of the `page` content type, such as the home story.

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

```typescript
import { Component, ChangeDetectionStrategy, input } from '@angular/core';
import { type SbBlokData, StoryblokComponent } from '@storyblok/angular';

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

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

The `PageComponent` passes the array of blocks in the `body` block field to the `StoryblokComponent`, which dynamically renders each block. It uses Angular signal for its `blok` input, ensuring the template updates automatically when the input changes.

Stories might contain a `body` or similar field that consists of an array with several blocks of custom types (for example, Feature, Teaser, Grid) in it.

Create the code for these components as follows.

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

```typescript
import { Component, ChangeDetectionStrategy, input } from '@angular/core';
import { type SbBlokData } from '@storyblok/angular';

export interface FeatureBlok extends SbBlokData {
  name?: string;
  description?: string;
}

@Component({
  selector: 'app-feature',
  changeDetection: ChangeDetectionStrategy.OnPush,
  template: `
    <div class="feature">
      <h3>{{ blok().name }}</h3>
      <p>{{ blok().description }}</p>
    </div>
  `,
})
export class FeatureComponent {
  readonly blok = input.required<FeatureBlok>();
}
```

src/app/components/teaser/teaser.component.ts

```typescript
import { Component, ChangeDetectionStrategy, input } from '@angular/core';
import { type SbBlokData } from '@storyblok/angular';

export interface TeaserBlok extends SbBlokData {
  headline?: string;
}

@Component({
  selector: 'app-teaser',
  changeDetection: ChangeDetectionStrategy.OnPush,
  template: `
    <div class="teaser">
      <h1>{{ blok().headline }}</h1>
    </div>
  `,
})
export class TeaserComponent {
  readonly blok = input.required<TeaserBlok>();
}
```

src/app/components/grid/grid.component.ts

```typescript
import { Component, ChangeDetectionStrategy, input } from '@angular/core';
import { StoryblokComponent, type SbBlokData } from '@storyblok/angular';

export interface GridBlok extends SbBlokData {
  columns?: SbBlokData[];
}

@Component({
  selector: 'app-grid',
  changeDetection: ChangeDetectionStrategy.OnPush,
  imports: [StoryblokComponent],
  template: `<sb-component class="grid" [sbBlok]="blok().columns" />`,
})
export class GridComponent {
  readonly blok = input.required<GridBlok>();
}
```

Similar to `page.component.ts`, `grid.component.ts` passes the array of blocks in the `columns` block field to the `StoryblokComponent`.

Create a component registry for Storyblok components in `storyblok.components.ts` and use lazy loading to reduce the initial bundle size.

src/app/storyblok.components.ts

```typescript
import { type StoryblokComponentsMap } from '@storyblok/angular';

export const storyblokComponents: StoryblokComponentsMap = {
  page: () => import('./components/page/page.component').then((m) => m.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),
};
```

Update `app.config.ts` to register Storyblok components. Pass the `storyblokComponents` registry to the `withStoryblokComponents` provider.

By default, Angular does not bind route data to component inputs.  
Add `withComponentInputBinding()` to the `app.config.ts` file to allow resolved data to be passed directly as component inputs. `withComponentInputBinding()` automatically binds route resolver data to matching component inputs.

src/app/app.config.ts

```typescript
import { ApplicationConfig, provideBrowserGlobalErrorListeners } from '@angular/core';
 import { provideRouter, withComponentInputBinding } from '@angular/router';
 import { provideRouter } from '@angular/router';

import { routes } from './app.routes';
import { provideClientHydration, withEventReplay } from '@angular/platform-browser';
import { environment } from '../environments/environment';

 import { provideStoryblok, withStoryblokComponents } from '@storyblok/angular';
 import { provideStoryblok } from '@storyblok/angular';
 import { storyblokComponents } from './storyblok.components'

export const appConfig: ApplicationConfig = {
  providers: [
    provideBrowserGlobalErrorListeners(),
    provideRouter(routes, withComponentInputBinding()),
    provideRouter(routes),
    provideClientHydration(withEventReplay()),
    provideStoryblok(
      {
        accessToken: environment.accessToken,
        region: 'eu',
      },
    withStoryblokComponents(storyblokComponents),
    ),
  ],
};
```

Update `src/app/app.routes.ts` file to define the routes, so that the root path `/` renders the `HomeComponent`.

src/app/app.routes.ts

```typescript
import { Routes } from '@angular/router';
import { HomeComponent } from './routes/home.component';

export const routes: Routes = [
  { path: '', component: HomeComponent },
];
```

Finally, remove the contents in `src/app/app.html` and add the `router-outlet`. The router uses it to render the component that matches the current URL based on the defined routes.

src/app/app.html

```html
<router-outlet></router-outlet>
```

Run the server and visit the site in your browser.

```bash
npm start
```

## Related Resources

[Storyblok's Angular Blueprint Repository](https://github.com/storyblok/blueprint-core-angular)

[@storyblok/angular Package Reference](https://www.storyblok.com/docs/libraries/js/angular-sdk)

[Concept: Blocks](/docs/concepts/blocks)

[Content Delivery API: Retrieve a Single Story](/docs/api/content-delivery/v2/stories/retrieve-a-single-story)

[Angular Docs](https://angular.dev/overview)

  

## Pagination

-   [Next: Visual Preview in Angular](/docs/guides/angular/visual-preview)
