Skip to content

@storyblok/svelte is Storyblok’s official development for Svelte applications.

  • Svelte version 5.0 or later
  • Node.js LTS (version 22.x recommended)
  • Modern web browser (e.g., Chrome, Firefox, Safari, Edge – latest versions)

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

Terminal window
npm install @storyblok/svelte@latest

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

src/layouts/+layout.js
import { apiPlugin, storyblokInit } from "@storyblok/svelte";
import Page from "./Page.svelte";
import Feature from "./Feature.svelte";
storyblokInit({
accessToken: "YOUR_ACCESS_TOKEN",
region: "eu",
use: [apiPlugin],
components: {
page: Page,
feature: Feature,
},
});

Create a Svelte component for each block defined in Storyblok and registered in the configuration (include the page block). Each component will receive a blok prop, containing the content of the block.

src/lib/Feature.svelte
<script>
import { storyblokEditable } from "@storyblok/svelte";
export let blok;
</script>
<div use:storyblokEditable={blok}>
<h2>{blok.name}</h2>
</div>

In a +page.js file, use the client to fetch a story and render the content using StoryblokComponent.

src/routes/+page.js
/** @type {import('./$types').PageLoad} */
export async function load({ parent }) {
const { storyblokAPI } = await parent();
const response = await storyblokAPI.get("cdn/stories/home");
return {
story: response.data.story,
};
}

Then render the content in +page.svelte with StoryblokComponent:

src/routes/+page.svelte
<script>
import { StoryblokComponent } from "@storyblok/svelte";
export let data;
</script>
<StoryblokComponent blok={data.story.content} />

storyblokInit() creates an instance of the Storyblok API client and loads the Storyblok Bridge.

import { storyblokInit } from "@storyblok/svelte";
storyblokInit(OPTIONS);

All options listed in the @storyblok/js reference are available. The following additional options are available:

KeyDescriptionType
componentsAn object that maps Svelte components to Storyblok blocks. Each component receives a blok prop containing the content of the block.object

apiPlugin configures the implementation of the Storyblok API. It is imported from @storyblok/js.

import { storyblokInit, apiPlugin } from "@storyblok/svelte";
storyblokInit({
use: [apiPlugin],
});

See the @storyblok/js reference for further details.

useStoryblokApi() returns the client instantiated in the application.

useStoryblok(URL, API_OPTIONS, BRIDGE_OPTIONS);

For the API_OPTIONS, see the storyblok-js-client reference.

getStoryblokApi() is an alias of useStoryblokApi().

useStoryblokBridge() activates the Storyblok Bridge.

import { onMount } from "svelte";
import { useStoryblokBridge } from "@storyblok/svelte";
onMount(() => {
useStoryblokBridge(STORY_ID, CALLBACK, BRIDGE_OPTIONS);
});

For the BRIDGE_OPTIONS, see the @storyblok/preview-bridge reference.

StoryblokComponent is a Svelte component that dynamically renders blocks from Storyblok. It accepts a blok prop, which should be a block from the Storyblok API. Any other props passed to StoryblokComponent will be passed directly to the block component.

<StoryblokComponent blok={blok} />

Use it to iterate over blocks fields as follows:

<script>
import { StoryblokComponent } from "@storyblok/svelte";
export let blok;
</script>
{#each blok.body as blok}
<StoryblokComponent {blok} />
{/each}

storyblokEditable() accepts a block from the Storyblok API and returns an object containing the HTML attributes to make elements editable in the Storyblok Visual Editor. See the @storyblok/js reference for further details.

As a Svelte action, storyblokEditable() should be called with Svelte’s use: directive, provided with a blok property:

<script>
import { storyblokEditable } from "@storyblok/svelte";
export let blok;
</script>
<div use:storyblokEditable={blok}>{blok.title}</div>

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

<script lang="ts">
import { StoryblokRichText } from "@storyblok/svelte";
</script>
<StoryblokRichText document={blok.richtext_field} />

The component accepts the following optional props:

  • components: Allows custom Svelte components to be provided for supported rich text nodes and marks.
  • optimizeImage: Allows image optimization options to be configured for image nodes.

See the @storyblok/richtext reference for a complete list of supported nodes, marks, and customization options.

Custom components can be registered using the components prop. The key must match a supported rich text node or mark name.

<script lang="ts">
import type { SbSvelteRichTextComponentMap } from "@storyblok/svelte";
import CustomHeading from "./CustomHeading.svelte";
import CustomLink from "./CustomLink.svelte";
import CustomTable from "./CustomTable.svelte";
const components: SbSvelteRichTextComponentMap = {
heading: CustomHeading,
link: CustomLink,
table: CustomTable,
};
</script>

The components can then be passed to StoryblokRichText:

<StoryblokRichText document={blok.richtext_field} components={components} />

Marks receive their rendered child content through Svelte’s {@render ...} snippet.

<script lang="ts">
import type { SbSvelteRichTextProps } from "@storyblok/svelte";
const props: SbSvelteRichTextProps<'link'> = $props();
</script>
<a href={props.attrs?.href ?? ""} target={props.attrs?.target ?? "_self"}>
{@render props.children?.()}
</a>

Nodes can use Svelte’s {@render ...} snippet to render child content.

<script lang="ts">
import type { SbSvelteRichTextProps } from "@storyblok/svelte";
const props: SbSvelteRichTextProps<'heading'> = $props();
const tag = $derived(`h${props.attrs?.level ?? 1}`);
</script>
<svelte:element
this={tag}
data-type="custom-heading"
data-level={props.attrs?.level}
>
{@render props.children?.()}
</svelte:element>

Example: Custom table component (advanced node)

Section titled “Example: Custom table component (advanced node)”

For advanced use cases, nodes can access content and context to recursively render child nodes using StoryblokRichText.

The splitTableRows utility can be used to separate table header rows from body rows when creating custom table components.

<script lang="ts">
import {
type SbSvelteRichTextProps,
StoryblokRichText,
splitTableRows,
} from "@storyblok/svelte";
const props: SbSvelteRichTextProps<"table"> = $props();
const { headerRows, bodyRows } = $derived(
splitTableRows(props.content)
);
</script>
<table class="custom-table">
{#if headerRows}
<thead>
<StoryblokRichText
document={headerRows}
{...props.context}
/>
</thead>
{/if}
<tbody>
<StoryblokRichText
document={bodyRows}
{...props.context}
/>
</tbody>
</table>

The Svelte 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.

Was this page helpful?

What went wrong?

This site uses reCAPTCHA and Google's Privacy Policy (opens in a new window) . Terms of Service (opens in a new window) apply.