@storyblok/vue (Version 10.x)
@storyblok/vue is Storyblok’s official development for Vue applications.
Requirements
Section titled “Requirements”- Vue version 3.4 or later
- Node.js LTS (version 22.x recommended)
- Modern web browser (e.g., Chrome, Firefox, Safari, Edge – latest versions)
Installation
Section titled “Installation”Add the package to a project by running this command in the terminal:
npm install @storyblok/vue@latestConfiguration
Section titled “Configuration”Import and initialize the SDK using the access token of a Storyblok space.
import { createApp } from "vue";import { StoryblokVue, apiPlugin } from "@storyblok/vue";import App from "./App.vue";
const app = createApp(App);
app.use(StoryblokVue, { accessToken: "YOUR_ACCESS_TOKEN", use: [apiPlugin], apiOptions: { region: "eu", },});
app.component("Page", Page);app.component("Feature", Feature);
app.mount("#app");Components
Section titled “Components”Create a Vue 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.
<script setup> defineProps({ blok: Object });</script>
<template> <div v-editable="blok"> <h2>{blok.headline}</h2> </div></template>Use <StoryblokComponent> to automatically render nested components (provided they are registered globally).
<script setup> defineProps({ blok: Object });</script>
<template> <main> <StoryblokComponent v-for="currentBlok in blok.body" :key="currentBlok._uid" :blok="currentBlok" /> </main></template>Using slots
Section titled “Using slots”You can use slots to insert content into the dynamic component:
<template> <StoryblokComponent v-if="story" :blok="story.content"> <MyCustomComponent /> </StoryblokComponent></template>Then, in the dynamic component that StoryblokComponent uses, you can render the slot content as you would with regular Vue slots:
<template> <div> <!-- Some content -->
<!-- The slot content MyCustomComponent will be rendered here --> <slot></slot>
<!-- Some more content --> </div></template>Fetching and rendering
Section titled “Fetching and rendering”In a Vue component, use the client to fetch a story and render the content using StoryblokComponent.
<script setup> import { useStoryblokApi } from '@storyblok/vue';
const storyblokApi = useStoryblokApi(); const { data } = await storyblokApi.get('cdn/stories/home', { version: 'draft', }); const { story } = data;</script>
<template> <StoryblokComponent v-if="story" :blok="story.content" /></template>StoryblokVue
Section titled “StoryblokVue”Import and initialize the SDK to access and configure all features.
import { createApp } from "vue";import { StoryblokVue } from "@storyblok/vue";import App from "./App.vue";import MyCustomFallback from "./components/MyCustomFallback.vue";
const app = createApp(App);
app.use(StoryblokVue, { // ... enableFallbackComponent: true, customFallbackComponent: "MyCustomFallback",});
app.component("MyCustomFallback", MyCustomFallback);All options listed in the @storyblok/js package reference are available. The following additional options are available:
| Key | Description | Type |
|---|---|---|
enableFallbackComponent | Enable or disable a fallback component to be rendered if no Vue component has been defined for a Storyblok block. Disabled by default. | boolean |
customFallbackComponent | Register a custom fallback component. Requires enableFallbackComponent to be enabled. See example below. | string |
Example: customFallbackComponent
Section titled “Example: customFallbackComponent”import MyCustomFallback from "./components/MyCustomFallback.vue";
app.use(StoryblokVue, { // ... enableFallbackComponent: true, customFallbackComponent: "MyCustomFallback",});
app.component("MyCustomFallback", MyCustomFallback);apiPlugin
Section titled “apiPlugin”apiPlugin configures the implementation of the Storyblok API. It is imported from @storyblok/js.
import { StoryblokVue, apiPlugin } from "@storyblok/vue";
app.use(StoryblokVue, { use: [apiPlugin],});See the @storyblok/js reference for further details.
useStoryblok
Section titled “useStoryblok”Enable both data fetching and bridge capabilities using this composable.
<script setup> import { useStoryblok } from "@storyblok/vue"; const { story, fetchState } = useStoryblok(URL, API_OPTIONS, BRIDGE_OPTIONS);</script>For the API_OPTIONS, see the storyblok-js-client reference. For the BRIDGE_OPTIONS, see the @storyblok/preview-bridge reference.
useStoryblokApi
Section titled “useStoryblokApi”useStoryblokApi() returns the client instantiated in the application.
<script setup> import {useStoryblokApi} from '@storyblok/vue'; const storyblokApi = useStoryblokApi(); const {data} = await storyblokApi.get(URL, API_OPTIONS)</script>For the API_OPTIONS, see the storyblok-js-client reference.
useStoryblokBridge
Section titled “useStoryblokBridge”useStoryblokBridge() activates the Storyblok Bridge.
<script setup> import {useStoryblokApi, useStoryblokBridge} from '@storyblok/vue'; const storyblokApi = useStoryblokApi(); const {data} = await storyblokApi.get(URL, API_OPTIONS); useStoryblokBridge(STORY_ID, CALLBACK, BRIDGE_OPTIONS);</script>For the BRIDGE_OPTIONS, see the @storyblok/preview-bridge reference.
StoryblokComponent
Section titled “StoryblokComponent”This component automatically renders Storyblok blocks for corresponding Vue components registered in the application. It requires a blok property. Any additional passed properties are forwarded to the Vue component.
Use it to iterate over blocks fields as follows:
<StoryblokComponent v-for="currentBlok in blok.nested_bloks" :key="currentBlok._uid" :blok="currentBlok"/>v-editable
Section titled “v-editable”Use the v-editable directive in components to connect them to the Storyblok Bridge.
<script setup> defineProps({ blok: Object });</script>
<template> <section v-editable="blok"> <h3>{{ blok.name }}</h3> </section></template>StoryblokRichText
Section titled “StoryblokRichText”Used to render a rich text field from a Storyblok story.
<template> <StoryblokRichText :document="blok.richtext_field" /></template>The component accepts the following optional props:
components: Allows custom Vue 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
Section titled “Custom components”Custom components can be registered using the components prop. The key must match a supported rich text node or mark name.
import type { SbVueRichTextComponentMap } from "@storyblok/vue";
import CustomHeading from "./CustomHeading.vue";import CustomLink from "./CustomLink.vue";import CustomTable from "./CustomTable.vue";import { h } from "vue";
const components: SbVueRichTextComponentMap = { heading: CustomHeading, link: CustomLink, table: CustomTable, paragraph: ({ content }, { slots }) => h( "p", { class: "custom-paragraph", }, slots.default?.() ),};The components can then be passed to StoryblokRichText:
<template> <StoryblokRichText :document="blok.richtext_field" :components="components" /></template>Example: Custom link component (mark)
Section titled “Example: Custom link component (mark)”Marks receive their rendered child content through Vue’s <slot />.
<script setup lang="ts">import type { SbVueRichTextProps } from "@storyblok/vue";
const props = defineProps<SbVueRichTextProps["link"]>();</script>
<template> <a :href="props.attrs?.href ?? ''" :target="props.attrs?.target ?? '_self'"> <slot /> </a></template>Example: Custom heading component (node)
Section titled “Example: Custom heading component (node)”Nodes can use Vue’s <slot /> to render child content.
<script setup lang="ts">import type { SbVueRichTextProps } from "@storyblok/vue";
const props = defineProps<SbVueRichTextProps["heading"]>();</script>
<template> <component :is="`h${props.attrs?.level ?? 1}`" class="custom-heading"> <slot /> </component></template>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 setup lang="ts">import { StoryblokRichText, splitTableRows, type SbVueRichTextProps } from "@storyblok/vue";
const props = defineProps<SbVueRichTextProps["table"]>();
const { headerRows, bodyRows } = splitTableRows(props.content);</script>
<template> <table class="custom-table"> <thead v-if="headerRows"> <StoryblokRichText :document="headerRows" v-bind="props.context" /> </thead>
<tbody> <StoryblokRichText :document="bodyRows" v-bind="props.context" /> </tbody> </table></template>useStoryblokRichText
Section titled “useStoryblokRichText”useStoryblokRichText can be used to programmatically render rich text content.
<script setup lang="ts">import { useStoryblokRichText, type SbVueRichTextComponentMap } from "@storyblok/vue";
import CustomLink from "../components/richtext/CustomLink.vue";
const components: SbVueRichTextComponentMap = { link: CustomLink,};
const render = useStoryblokRichText({ components });
const RichText = () => render(story.value?.content.richText);</script>
<template> <RichText /></template>Accepts the same configuration options as StoryblokRichText, including components and optimizeImage.
Utility helpers
Section titled “Utility helpers”The Vue 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 intoheaderRowsandbodyRows.
Further resources
Section titled “Further resources”Previous versions
Section titled “Previous versions”Was this page helpful?
This site uses reCAPTCHA and Google's Privacy Policy (opens in a new window) . Terms of Service (opens in a new window) apply.
Get in touch with the Storyblok community