Skip to content

@storyblok/nuxt (Version 8.x)

@storyblok/nuxt is Storyblok’s official development for Nuxt applications.

  • Nuxt version 3.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/nuxt@latest

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

nuxt.config.ts
import { defineNuxtConfig } from "nuxt";
export default defineNuxtConfig({
modules: [
[
"@storyblok/nuxt",
{
accessToken: "YOUR_ACCESS_TOKEN",
apiOptions: {
region: "eu",
},
},
],
],
});

Create a Nuxt component for each block defined in Storyblok. Each component will receive a blok prop, containing the content of the block.

storyblok/Feature.vue
<script setup>
defineProps({ blok: Object });
</script>
<template>
<div v-editable="blok">
<h2>{blok.headline}</h2>
</div>
</template>

Use <StoryblokComponent> to automatically render nested components.

storyblok/Page.vue
<script setup>
defineProps({ blok: Object });
</script>
<template>
<main>
<StoryblokComponent v-for="currentBlok in blok.body" :key="currentBlok._uid" :blok="currentBlok" />
</main>
</template>

In a Nuxt page or component, use the one-liner composable useAsyncStoryblok to fetch a story and render the content with the StoryblokComponent element.

pages/index.vue
<script setup>
const { story } = await useAsyncStoryblok("home", {
// asyncData options https://nuxt.com/docs/api/composables/use-async-data#params
lazy: false,
api: {
version: "draft",
resolve_relations: "featured-articles.posts",
},
bridge: {
resolveRelations: ["featured-articles.posts"],
},
});
</script>
<template>
<StoryblokComponent v-if="story" :blok="story.content" />
</template>

Updated in 8.2.0

Import and initialize the SDK to access and configure all features.

import { defineNuxtConfig } from "nuxt";
export default defineNuxtConfig({
modules: [["@storyblok/nuxt", OPTIONS]],
});

Alternative syntax:

import { defineNuxtConfig } from "nuxt";
export default defineNuxtConfig({
modules: ["@storyblok/nuxt"],
storyblok: OPTIONS,
});

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

KeyDescriptionType
componentsDirDefine a custom directory other than storyblok as the location of components.string
enableServerModeEnables the JavaScript client to be used on the Nitro API server (and disables it from being used client-side). Defaults to false.boolean
enableSudoModeSee example below.boolean

Use it to apply your custom API customizations.

plugins/storyblok.js
import { apiPlugin, StoryblokVue } from '@storyblok/vue';
export default defineNuxtPlugin(({ vueApp }) => {
vueApp.use(StoryblokVue, {
accessToken: '<access-token>',
enableSudoMode: true,
apiOptions: {
cache: {
type: 'custom',
custom: {
flush() {
console.log('done');
}
}
}
},
use: [apiPlugin]
});

Import and use StoryblokVue to create a custom Nuxt plugin as shown in the enableSudoMode example above.

Using this one-liner composable, enable both data fetching and bridge capabilities. This is the recommended option, as it supports both server-side rendering (SSR) and static site generation (SSG).

<script setup>
const { story, error } = await useAsyncStoryblok(URL, API_OPTIONS, BRIDGE_OPTIONS);
if (error.value) {
throw createError({
statusCode: error.value.statusCode,
statusMessage: error.value.statusMessage
});
}
}
</script>

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

Enable both data fetching and bridge capabilities using this composable. Recommended only for client-side rendering (CSR).

<script setup>
import {useStoryblok} from '@storyblok/vue';
const {(story, fetchState)} = await 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() returns the client instantiated in the application.

<script setup>
import { useStoryblokApi } from "@storyblok/nuxt";
const storyblokApi = useStoryblokApi();
const { data } = await storyblokApi.get(URL, API_OPTIONS);
</script>

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

Introduced in 8.2.0

Get an instance of the Storyblok client for Nitro API Server endpoints with the explicit import #storyblok/server.

server/api/example.ts
import { serverStoryblokClient } from "#storyblok/server";
export default defineEventHandler(async (event) => {
const storyblokApi = serverStoryblokClient(event);
const { data } = await storyblokApi.get("cdn/stories/home", {
version: "draft",
});
return { story: data.story };
});

useStoryblokBridge() activates the Storyblok Bridge.

<script setup>
import { useStoryblokApi, useStoryblokBridge } from "@storyblok/nuxt";
const storyblokApi = useStoryblokApi();
const { data } = await storyblokApi.get(URL, API_OPTIONS);
onMounted(() => {
useStoryblokBridge(STORY_ID, CALLBACK, BRIDGE_OPTIONS);
});
</script>

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

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.

<StoryblokComponent :blok="story.content" />

Use it to iterate over blocks fields as follows:

<StoryblokComponent v-for="currentBlok in blok.body" :key="currentBlok._uid" :blok="currentBlok" />

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>

Used to render a rich text field from a story.

<StoryblokRichText :doc="blok.richtext_field" />

See the @storyblok/richtext reference for further details.

<script setup>
import { NuxtLink } from '#components';
import type { StoryblokRichTextNode } from '@storyblok/vue';
import CodeBlok from "./components/CodeBlok.vue";
const resolvers = {
// NuxtLink example:
[MarkTypes.LINK]: (node: StoryblokRichTextNode<VNode>) =>
h(NuxtLink, {
to: node.attrs?.href,
target: node.attrs?.target,
}, node.text),
// Custom code block component example:
[BlockTypes.CODE_BLOCK]: (node: Node) => {
return h(CodeBlock, {
class: node?.attrs?.class,
}, node.children)
},
}
</script>
<template>
<StoryblokRichText :doc="blok.richtext_field" :resolvers="resolvers" />
</template>

Use this composable to programmatically render a rich text field.

<script setup>
import { useStoryblokRichText } from "@storyblok/nuxt";
const { render } = useStoryblokRichText(RICH_TEXT_OPTIONS);
const content = render(blok.articleContent);
</script>
<template>
<div v-html="content"></div>
</template>

See the @storyblok/richtext reference for further details.

<script setup>
import CodeBlok from "./components/CodeBlok.vue";
const { render } = useStoryblokRichText({
resolvers: {
// NuxtLink example:
[MarkTypes.LINK]: (node: StoryblokRichTextNode<VNode>) =>
h(NuxtLink, {
to: node.attrs?.href,
target: node.attrs?.target,
}, node.text),
// Custom code block component example:
[BlockTypes.CODE_BLOCK]: (node: Node) =>
h(CodeBlock, {
class: node?.attrs?.class,
}, node.children)
}
});
const root = () => render(blok.articleContent);
</script>