Skip to content

@storyblok/nuxt (Version 7.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@7

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",
{ version: "draft", resolve_relations: "featured-articles.posts" },
{ resolveRelations: ["featured-articles.posts"]},
);
</script>
<template>
<StoryblokComponent v-if="story" :blok="story.content" />
</template>

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
enableSudoModeSee example below.boolean

To include additional functionalities in the apiOptions, such as custom cache methods, implement the following inside the plugins folder:

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

If desired, import and use StoryblokVue to create a custom Nuxt plugin as shown in the enableSudoMode example above.

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

import { apiPlugin } from "@storyblok/nuxt";
import { defineNuxtConfig } from "nuxt";
export default defineNuxtConfig({
modules: [
[
"@storyblok/nuxt",
{
accessToken: "<your-access-token>",
use: [apiPlugin],
},
],
],
});

See the @storyblok/js reference for further details.

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 = await useAsyncStoryblok(URL, API_OPTIONS, BRIDGE_OPTIONS);
</script>
<template>
<StoryblokComponent v-if="story" :blok="story.content" />
</template>

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.

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>