Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
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
- Host: GitHub
- URL: https://github.com/storyblok/storyblok-nuxt
- Owner: storyblok
- Created: 2018-01-07T19:08:58.000Z (almost 7 years ago)
- Default Branch: main
- Last Pushed: 2024-04-12T10:39:51.000Z (7 months ago)
- Last Synced: 2024-04-14T07:09:42.963Z (7 months ago)
- Topics: headless-cms, nuxt, nuxtjs, vue, vuejs
- Language: TypeScript
- Homepage: https://www.storyblok.com/tp/nuxt-js-multilanguage-website-tutorial
- Size: 8.67 MB
- Stars: 256
- Watchers: 22
- Forks: 39
- Open Issues: 22
-
Metadata Files:
- Readme: README.md
Awesome Lists containing this project
README
## 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 ChinaFull 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.jsconst 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.