Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/storyblok/storyblok-nuxt

Storyblok Nuxt module
https://github.com/storyblok/storyblok-nuxt

headless-cms nuxt nuxtjs vue vuejs

Last synced: about 1 month ago
JSON representation

Storyblok Nuxt module

Awesome Lists containing this project

README

        



Storyblok Logo

@storyblok/nuxt


Nuxt 3 module for the Storyblok, Headless CMS.





Storyblok JS Client


npm






Follow @Storyblok



Follow @Storyblok

## Kickstart a new project
Are you eager to dive into coding? **[Follow these steps to kickstart a new project with Storyblok and Nuxt](https://www.storyblok.com/technologies#nuxt?utm_source=github.com&utm_medium=readme&utm_campaign=storyblok-nuxt)**, and get started in just a few minutes!

## Ultimate Tutorial
Are you looking for a hands-on, step-by-step tutorial? The **[Nuxt Ultimate Tutorial](https://www.storyblok.com/tp/storyblok-nuxt-ultimate-tutorial?utm_source=github.com&utm_medium=readme&utm_campaign=storyblok-nuxt)** has you covered! It provides comprehensive instructions on building a complete, multilingual website using Storyblok and Nuxt from start to finish.

## Installation

Install `@storyblok/nuxt`:

```bash
npx nuxi@latest module add storyblok
```

Add following code to modules section of `nuxt.config.js` and replace the accessToken with API token from Storyblok space.

```js
import { defineNuxtConfig } from "nuxt";

export default defineNuxtConfig({
modules: [
["@storyblok/nuxt", { accessToken: "" }]
// ...
]
});
```

You can also use the `storyblok` config if you prefer:

```js
import { defineNuxtConfig } from "nuxt";

export default defineNuxtConfig({
modules: ["@storyblok/nuxt"],
storyblok: {
accessToken: ""
}
});
```

> **Warning**
> This SDK uses the Fetch API under the hood. If your environment doesn't support it, you need to install a polyfill like [isomorphic-fetch](https://github.com/matthew-andrews/isomorphic-fetch). More info on [storyblok-js-client docs](https://github.com/storyblok/storyblok-js-client#fetch-use-polyfill-if-needed---version-5).

### Options

When you initialize the module, you can pass all [_@storyblok/vue_ options](https://github.com/storyblok/storyblok-vue#storyblok-api) plus a `bridge` option explained in our [JS SDK Storyblok bridge section](https://github.com/storyblok/storyblok-js#storyblok-bridge) and a `enableSudoMode` option to define your own plugin (see below).

> **Note**
> If you want to use Storyblok inside `nuxt-devtools` you can use the option `devtools`, if enabled, make sure to have installed the @nuxt/devtools module and enable it on your nuxt config.

```js
// Defaults
["@storyblok/nuxt", {
{
accessToken: "",
bridge: true,
devtools: true,
apiOptions: {}, // storyblok-js-client options
}
}]
```

### Define your own plugin

While the recommended approach covers most cases, there are specific instances where you may need to use the `enableSudoMode` option and disable our plugin, allowing you to incorporate your own.

```js
// nuxt.config.ts
modules: [
[
"@storyblok/nuxt",
{
accessToken: "",
enableSudoMode: true
}
]
];
```

To include additional functionalities in the SDK's `apiOptions`, such as custom cache methods, you can implement the following solution inside the plugins folder (autoimported):

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

export default defineNuxtPlugin(({ vueApp }) => {
vueApp.use(StoryblokVue, {
accessToken: "",
apiOptions: {
cache: {
type: "custom",
custom: {
flush() {
console.log("all right");
}
}
}
},
use: [apiPlugin]
});
});
```

## Region parameter

Possible values:

- `eu` (default): For spaces created in the EU
- `us`: For spaces created in the US
- `ap`: For spaces created in Australia
- `ca`: For spaces created in Canada
- `cn`: For spaces created in China

Full example for a space created in the US:

```js
["@storyblok/nuxt", {
{
accessToken: "",
apiOptions: {
region: "us"
}
}
}]
```

> **Important**
> For spaces created in the United States or China, the `region` parameter **must** be specified.

## Getting started

### 1. Creating and linking your components to the Storyblok Visual Editor

To link your Vue components to the equivalent one in your Storyblok space:

- First, you need to load them globally adding them to the `~/storyblok` directory. It's important to name them with Pascal case in your code `ExampleComponent.vue` and with a hyphen inside your Storyblok space `example-component`, so they will be imported automatically.

If you want to define your own directory for the Storyblok related components, you can use the option `componentsDir` in the `nuxt.config.js`:

```js
// nuxt.config.ts
modules: [
[
"@storyblok/nuxt",
{
accessToken: "",
componentsDir: '~/components',
}
]
],
components: {
dirs: [
{
path: '~/components/storyblok',
global: true,
}
]
},
```

Otherwise, you can set another directory and load them manually (for example, by [using a Nuxt plugin](https://stackoverflow.com/questions/43040692/global-components-in-vue-nuxt)).

> **Warning**
> Take into account that if you name a component inside the `storyblok` folder the same as another in the `components` folder, it won't work properly. Tip: Keep the components in your Nuxt project with different names.

- For each component, use the `v-editable` directive on its root element, passing the `blok` property that they receive:

```html


```

- Finally, use `` which is available globally in the Nuxt app:

```html

```

> The `blok` is the actual blok data coming from [Storblok's Content Delivery API](https://www.storyblok.com/docs/api/content-delivery/v2?utm_source=github.com&utm_medium=readme&utm_campaign=storyblok-nuxt).

### 2. Getting Storyblok Stories and listen to Visual Editor events

#### Composition API

The simplest way is by using the `useAsyncStoryblok` one-liner composable (it's autoimported). Where you need to pass as first parameter the `slug`, while the second and third parameters, `apiOptions` and `bridgeOptions` respectively, are optional.

Check the available [apiOptions](https://www.storyblok.com/docs/api/content-delivery/v2#core-resources/stories/retrieve-one-story?utm_source=github.com&utm_medium=readme&utm_campaign=storyblok-nuxt) in our API docs and [bridgeOptions](https://www.storyblok.com/docs/Guides/storyblok-latest-js?utm_source=github.com&utm_medium=readme&utm_campaign=storyblok-nuxt) passed to the Storyblok Bridge.

> **Note**
> If you want to know more about versioning `{ version: "draft" /* or "publish" */ }` then go to the section [Working with preview and/or production environments](#3-working-with-preview-andor-production-environments)

```html

const story = await useAsyncStoryblok(
"vue",
{ version: "draft", resolve_relations: "Article.author" }, // API Options
{ resolveRelations: ["Article.author"], resolveLinks: "url" } // Bridge Options
);

if (story.value.status) {
throw createError({
statusCode: story.value.status,
statusMessage: story.value.response
});
}

```

Which is the short-hand equivalent to using `useStoryblokApi` inside `useState` and `useStoryblokBridge` functions separately:

```html

const story = useState();
const storyblokApi = useStoryblokApi();

const { data } = await storyblokApi.get(
`cdn/stories/vue`,
{
version: "draft"
}
);
story.value = data.story;

onMounted(() => {
useStoryblokBridge(
story.value.id,
(evStory) => (story.value = evStory),
{ resolveRelations: ["Article.author"], resolveLinks: "url" } // Bridge Options
);
});

```

> The `useState` is an SSR-friendly `ref` replacement. Its value will be preserved after server-side rendering (during client-side hydration).

## Rendering Rich Text

You can easily render rich text by using the `renderRichText` function that comes with `@storyblok/nuxt` and a Vue computed property:

```html

const props = defineProps({ blok: Object });
const articleContent = computed(() =>
renderRichText(props.blok.articleContent)
);

```

You can also set a **custom Schema and component resolver** by passing the options as the second parameter of the `renderRichText` function:

```html

import cloneDeep from "clone-deep";

const mySchema = cloneDeep(RichTextSchema); // you can make a copy of the default RichTextSchema
// ... and edit the nodes and marks, or add your own.
// Check the base RichTextSchema source here https://github.com/storyblok/storyblok-js-client/blob/v4/source/schema.js

const props = defineProps({ blok: Object });

const articleContent = computed(() =>
renderRichText(props.blok.articleContent, {
schema: mySchema,
resolver: (component, blok) => {
switch (component) {
case "my-custom-component":
return `<div class="my-component-class">${blok.text}</div>`;
default:
return "Resolver not defined";
}
}
})
);

```

## 3. Working with preview and/or production environments

Remember that the bridge only works using `version: 'draft'` and the _Preview Access Token_.

For the production site, NOT used as a preview for content editors, `version: 'published'` and _Public Access Token_ should be used.

> **Note**
> If you're using production as a preview for marketeers and your public site, you will need a plugin to handle different .env variables, or versions using the _Preview Access Token_, checking if you are inside Storyblok or not. For example, something like `if (window.location.search.includes(_storyblok_tk[token]=)`.

Check the official docs on how to [access different content versions](https://www.storyblok.com/docs/guide/essentials/accessing-data#content-versions).

The recommended way to handle different content versions with Nuxt is by using environment variables in combination with [Nuxt runtime config](https://nuxt.com/docs/guide/going-further/runtime-config) to expose configuration and secrets within your application

In your `nuxt.config.ts`:

```ts
export default defineNuxtConfig({
runtimeConfig: {
public: {
storyblokVersion: process.env.STORYBLOK_VERSION || "published"
}
}
});
```

Then you can access the runtime config in your components:

```ts
const config = useRuntimeConfig();

const story = await useAsyncStoryblok(
"blog",
{
version: config.public.storyblokVersion,
resolve_relations: "overview.featured_story"
},
{ resolveRelations: "overview.featured_story" }
);

//or

const { data: articles } = await storyblokApi.get("cdn/stories", {
version: config.public.storyblokVersion,
starts_with: "blog",
is_startpage: false
});
```

## API

### useAsyncStoryblok(slug, apiOptions, bridgeOptions)

(Recommended Option) Uses [`useState`](https://v3.nuxtjs.org/api/composables/use-state) under the hood to help with SSR compatibility.

Check the available [apiOptions](https://www.storyblok.com/docs/api/content-delivery/v2#core-resources/stories/retrieve-one-story?utm_source=github.com&utm_medium=readme&utm_campaign=storyblok-nuxt) (passed to `storyblok-js-client`) and [bridgeOptions](https://www.storyblok.com/docs/Guides/storyblok-latest-js?utm_source=github.com&utm_medium=readme&utm_campaign=storyblok-nuxt) (passed to the Storyblok Bridge).

### useStoryblok(slug, apiOptions, bridgeOptions)

It could be helpful to use `useStoryblok` instead of `useAsyncStoryblok` when we need to make full client-side requests, for example, getting personalized data for a logged user.

Check the available [apiOptions](https://www.storyblok.com/docs/api/content-delivery/v2#core-resources/stories/retrieve-one-story?utm_source=github.com&utm_medium=readme&utm_campaign=storyblok-nuxt) (passed to `storyblok-js-client`) and [bridgeOptions](https://www.storyblok.com/docs/Guides/storyblok-latest-js?utm_source=github.com&utm_medium=readme&utm_campaign=storyblok-nuxt) (passed to the Storyblok Bridge).

### useStoryblokApi()

Returns the instance of the `storyblok-js-client`.

### useStoryblokBridge(storyId, callback, bridgeOptions)

Use this one-line function to cover the most common use case: updating the story when any kind of change happens on Storyblok Visual Editor.

## The Storyblok JavaScript SDK Ecosystem

![A visual representation of the Storyblok JavaScript SDK Ecosystem](https://a.storyblok.com/f/88751/2400x1350/be4a4a4180/sdk-ecosystem.png/m/1200x0)

## Further Resources

- [Quick Start](https://www.storyblok.com/technologies?utm_source=github.com&utm_medium=readme&utm_campaign=storyblok-nuxt)
- [API Documentation](https://www.storyblok.com/docs/api?utm_source=github.com&utm_medium=readme&utm_campaign=storyblok-nuxt)
- [Developer Tutorials](https://www.storyblok.com/tutorials?utm_source=github.com&utm_medium=readme&utm_campaign=storyblok-nuxt)
- [Developer Guides](https://www.storyblok.com/docs/guide/introduction?utm_source=github.com&utm_medium=readme&utm_campaign=storyblok-nuxt)
- [FAQs](https://www.storyblok.com/faqs?utm_source=github.com&utm_medium=readme&utm_campaign=storyblok-nuxt)

## Support

- Bugs or Feature Requests? [Submit an issue](/../../issues/new);
- Do you have questions about Storyblok or you need help? [Join our Discord Community](https://discord.gg/jKrbAMz).

## Contributing

Please see our [contributing guidelines](https://github.com/storyblok/.github/blob/master/contributing.md) and our [code of conduct](https://www.storyblok.com/trust-center#code-of-conduct?utm_source=github.com&utm_medium=readme&utm_campaign=storyblok-nuxt).
This project use [semantic-release](https://semantic-release.gitbook.io/semantic-release/) for generate new versions by using commit messages and we use the Angular Convention to naming the commits. Check [this question](https://semantic-release.gitbook.io/semantic-release/support/faq#how-can-i-change-the-type-of-commits-that-trigger-a-release) about it in semantic-release FAQ.