---
title: @storyblok/vue (Version 10.x)
description: @storyblok/vue is Storyblok's official development kit for Vue applications.
url: https://storyblok.com/docs/libraries/js/vue-sdk
---

# @storyblok/vue (Version 10.x)

[@storyblok/vue](https://github.com/storyblok/monoblok/tree/main/packages/vue) is Storyblok’s official development for Vue applications.

## 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

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

```bash
npm install @storyblok/vue@latest
```

## Usage

### Configuration

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

src/main.js

```js
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");
```

> [!TIP]
> Learn how to retrieve an access token in the [access tokens concept](/docs/concepts/access-tokens).

> [!WARNING]
> The `region` parameter must be specified unless the space was created in the EU. Learn more in the [@storyblok/js reference](/docs/libraries/js/js-sdk).

### 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.

src/components/Feature.vue

```vue-html
<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).

src/components/Page.vue

```vue-html
<script setup>
  defineProps({ blok: Object });
</script>

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

#### Using slots

You can use slots to insert content into the dynamic component:

```vue-html
<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:

```vue-html
<template>
  <div>
    <!-- Some content <!-- Some content -->

    <!-- The slot content MyCustomComponent will be rendered here <!-- The slot content MyCustomComponent will be rendered here -->
    <slot></slot>

    <!-- Some more content <!-- Some more content -->
  </div>
</template>
```

### Fetching and rendering

In a Vue component, use the client to fetch a story and render the content using `StoryblokComponent`.

src/App.vue

```vue-html
<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>
```

## API

### `StoryblokVue`

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

```js
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](/docs/libraries/js/js-sdk) 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`

```js
import MyCustomFallback from "./components/MyCustomFallback.vue";

app.use(StoryblokVue, {
  // ...
  enableFallbackComponent: true,
  customFallbackComponent: "MyCustomFallback",
});

app.component("MyCustomFallback", MyCustomFallback);
```

### `apiPlugin`

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

```js
import { StoryblokVue, apiPlugin } from "@storyblok/vue";

app.use(StoryblokVue, {
  use: [apiPlugin],
});
```

See the [@storyblok/js reference](/docs/libraries/js/js-sdk) for further details.

### `useStoryblok`

Enable both data fetching and bridge capabilities using this composable.

```vue-html
<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](https://github.com/storyblok/monoblok/tree/main/packages/js-client). For the `BRIDGE_OPTIONS`, see the [@storyblok/preview-bridge reference](/docs/libraries/js/preview-bridge).

### `useStoryblokApi`

`useStoryblokApi()` returns the client instantiated in the application.

```vue-html
<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](https://github.com/storyblok/monoblok/tree/main/packages/js-client).

### `useStoryblokBridge`

`useStoryblokBridge()` activates the Storyblok Bridge.

```vue-html
<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](/docs/libraries/js/preview-bridge).

### `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:

```vue-html
<StoryblokComponent
  v-for="currentBlok in blok.nested_bloks"
  :key="currentBlok._uid"
  :blok="currentBlok"
/>
```

### `v-editable`

Use the `v-editable` directive in components to connect them to the Storyblok Bridge.

```vue-html
<script setup>
  defineProps({ blok: Object });
</script>

<template>
  <section v-editable="blok">
    <h3>{{ blok.name }}</h3>
  </section>
</template>
```

### `StoryblokRichText`

Used to render a rich text field from a story.

```vue-html
<StoryblokRichText :doc="blok.richtext_field" />
```

See the [@storyblok/richtext reference](/docs/libraries/js/rich-text) for further details.

#### Example: Custom link with RouterLink

Use `asTag` to render a `RouterLink` component for internal story links:

```vue-html
<script setup>
  import { Mark } from '@tiptap/core';
  import { asTag } from '@storyblok/vue';
  import { RouterLink } from 'vue-router';

  const CustomLink = Mark.create({
    name: 'link',
    renderHTML({ HTMLAttributes }) {
      if (HTMLAttributes.linktype === 'story') {
        return [asTag(RouterLink), { to: HTMLAttributes.href }, 0];
      }
      return ['a', { href: HTMLAttributes.href, target: HTMLAttributes.target }, 0];
    },
  });
</script>

<template>
  <StoryblokRichText :doc="blok.richtext_field" :tiptap-extensions="{ link: CustomLink }" />
</template>
```

### `useStoryblokRichText`

Use this composable to programmatically render a rich text field.

```vue-html
<script setup>
  import { useStoryblokRichText } from '@storyblok/vue';

  const { render } = useStoryblokRichText({});

  const root = () => render(blok.articleContent);
</script>

<template>
  <root />
</template>
```

See the [@storyblok/richtext reference](/docs/libraries/js/rich-text) for further details.

#### Example: Custom tiptap extensions

```vue-html
<script setup>
  import Heading from '@tiptap/extension-heading';
  import { useStoryblokRichText } from '@storyblok/vue';

  const CustomHeading = Heading.extend({
    renderHTML({ node, HTMLAttributes }) {
      const level = node.attrs.level;
      return [`h${level}`, { class: `heading-${level}`, ...HTMLAttributes }, 0];
    },
  });

  const { render } = useStoryblokRichText({
    tiptapExtensions: { heading: CustomHeading },
  });

  const root = () => render(blok.articleContent);
</script>

<template>
  <root />
</template>
```

## Further resources

[Repository Playground](https://github.com/storyblok/monoblok/tree/main/packages/vue/playground) See the repository playground for additional examples.

[Vue Guide](/docs/guides/vue/) See the Vue guide for a comprehensive walkthrough on integrating Storyblok with Vue.

[Space Blueprint: Vue](https://github.com/storyblok/blueprint-core-vue) See the core space blueprint for Vue to kickstart a new project.

## Previous versions

[@storyblok/vue (Version 9.x)](/docs/libraries/js/vue-sdk/v9)

## Pagination

-   [Previous: Nuxt SDK](/docs/libraries/js/nuxt-sdk)
-   [Next: Svelte SDK](/docs/libraries/js/svelte-sdk)
