npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@storyblok/angular

v0.1.1

Published

Official Angular integration for the Storyblok Headless CMS

Readme

Storyblok Angular

Features

  • Fetch content from the Content Delivery API using StoryblokService
  • Dynamic component rendering with SbBlokDirective
  • Real-time Visual Editing with LivePreviewService
  • Rich text rendering with SbRichTextComponent
  • Full SSR support with Angular Universal
  • Tree-shakeable features for optimal bundle size
  • Lazy loading support for Storyblok components

Installation

Install @storyblok/angular:

npm install @storyblok/angular
# or
pnpm add @storyblok/angular
# or
yarn add @storyblok/angular

Setup

1. Configure the SDK

Add the Storyblok provider to your application configuration:

// app.config.ts
import { ApplicationConfig } from '@angular/core';
import {
  provideStoryblok,
  withStoryblokComponents,
  withLivePreview,
} from '@storyblok/angular';

export const appConfig: ApplicationConfig = {
  providers: [
    provideStoryblok(
      {
        accessToken: 'YOUR_ACCESS_TOKEN',
        region: 'eu', // 'eu', 'us', 'ap', 'ca', or 'cn'
      },
      withStoryblokComponents({
        page: () => import('./components/page').then((m) => m.PageComponent),
        teaser: () => import('./components/teaser').then((m) => m.TeaserComponent),
      }),
      withLivePreview()
    ),
  ],
};

2. Create Storyblok components

Create Angular components that match your Storyblok component names:

// components/teaser.ts
import { Component, input } from '@angular/core';
import { type SbBlokData } from '@storyblok/angular';

interface TeaserBlok extends SbBlokData {
  headline?: string;
}

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

3. Fetch and render content

Use StoryblokService to fetch content and SbBlokDirective to render it:

// routes/home.component.ts
import { Component, inject, signal, OnInit } from '@angular/core';
import { StoryblokService, SbBlokDirective, type SbBlokData } from '@storyblok/angular';

@Component({
  selector: 'app-home',
  imports: [SbBlokDirective],
  template: `<ng-container [sbBlok]="content()" />`,
})
export class HomeComponent implements OnInit {
  private readonly storyblok = inject(StoryblokService);
  readonly content = signal<SbBlokData | null>(null);

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

Note: For spaces created in the United States or China, the region parameter must be specified.

Live Preview

Enable real-time visual editing in the Storyblok Visual Editor:

// app.config.ts
provideStoryblok(
  { accessToken: 'YOUR_ACCESS_TOKEN' },
  withLivePreview({
    resolveRelations: ['article.author'],
  })
)

In your route component, listen for live updates:

import { Component, inject, linkedSignal, input, OnInit } from '@angular/core';
import { LivePreviewService, SbBlokDirective, type ISbStoryData } from '@storyblok/angular';

@Component({
  imports: [SbBlokDirective],
  template: `<ng-container [sbBlok]="story()?.content" />`,
})
export class CatchAllComponent implements OnInit {
  private readonly livePreview = inject(LivePreviewService);
  
  readonly storyInput = input<ISbStoryData | null>(null, { alias: 'story' });
  readonly story = linkedSignal(() => this.storyInput());

  ngOnInit() {
    this.livePreview.listen((updatedStory) => {
      this.story.set(updatedStory);
    });
  }
}

Render rich text

Use the SbRichTextComponent to render rich text fields:

import { Component, input } from '@angular/core';
import { SbRichTextComponent, type StoryblokRichTextNode } from '@storyblok/angular';

@Component({
  imports: [SbRichTextComponent],
  template: `<sb-rich-text [doc]="content()" />`,
})
export class ArticleComponent {
  readonly content = input<StoryblokRichTextNode>();
}

Custom rich text components

Override default elements with custom Angular components:

// app.config.ts
import { withStoryblokRichtextComponents } from '@storyblok/angular';

provideStoryblok(
  { accessToken: 'YOUR_ACCESS_TOKEN' },
  withStoryblokRichtextComponents({
    link: CustomLinkComponent,
    image: OptimizedImageComponent,
  })
)

Create a custom component using the props input pattern:

import { Component, input, computed } from '@angular/core';
import { SbRichTextNodeComponent, type AngularRenderNode } from '@storyblok/angular';

interface LinkProps {
  href?: string;
  target?: string;
  children?: AngularRenderNode[];
}

@Component({
  imports: [SbRichTextNodeComponent],
  template: `
    <a [href]="href()" [target]="target()" class="custom-link">
      <sb-rich-text-node [nodes]="children()" />
    </a>
  `,
})
export class CustomLinkComponent {
  readonly props = input<LinkProps>({});
  readonly href = computed(() => this.props().href ?? '');
  readonly target = computed(() => this.props().target ?? '_self');
  readonly children = computed(() => this.props().children ?? []);
}

Lazy loading components

Register components with lazy loading for optimal bundle size:

const storyblokComponents: StoryblokComponentsMap = {
  page: () => import('./components/page').then((m) => m.PageComponent),
  teaser: () => import('./components/teaser').then((m) => m.TeaserComponent),
  grid: () => import('./components/grid').then((m) => m.GridComponent),
};

provideStoryblok(
  { accessToken: 'YOUR_ACCESS_TOKEN' },
  withStoryblokComponents(storyblokComponents)
)

API reference

Providers

| Provider | Description | |----------|-------------| | provideStoryblok(config, ...features) | Configure the SDK with access token and optional features | | withStoryblokComponents(map) | Register Storyblok components (eager or lazy) | | withLivePreview(options?) | Enable real-time visual editing | | withStoryblokRichtextComponents(map) | Register custom rich text components |

Services

| Service | Description | |---------|-------------| | StoryblokService | Access the Storyblok API client | | LivePreviewService | Listen for live preview updates |

Components and directives

| Component/Directive | Description | |---------------------|-------------| | SbBlokDirective | Dynamically render Storyblok components | | SbRichTextComponent | Render rich text content | | SbRichTextNodeComponent | Render rich text nodes (for custom components) |

Types

| Type | Description | |------|-------------| | SbBlokData | Base type for Storyblok block data | | ISbStoryData | Story data from the API | | StoryblokRichTextNode | Rich text document node | | AngularRenderNode | Rendered node in rich text AST |

Documentation

For complete documentation, visit the Storyblok Angular SDK documentation.

Contributing

If you'd like to contribute, refer to the contributing guidelines.

Community

For help, discussion about best practices, or any other conversation:

Support

For bugs or feature requests, please submit an issue.

[!IMPORTANT] Please search existing issues before submitting a new one. Issues without a minimal reproducible example will be closed. Why reproductions are Required.

I can't share my company project code

We understand that you might not be able to share your company's project code. Please provide a minimal reproducible example that demonstrates the issue by using tools like Stackblitz or a link to a GitHub repo. Please make sure you include a README file with the instructions to build and run the project, important not to include any access token, password or personal information of any kind.

Feedback

If you have a question, please ask in the Discuss Storyblok on Discord channel.

License

License