Gridsome Source Storyblok
The official Storyblok integration with Gridsome.
To see it in action take a look at the Storyblok Gridsome Boilerplate.

Install
yarn add gridsome-source-storyblok
Usage
- In
gridsome.config.js
, declare the use of the plugin and define the options:
{
siteName: 'Gridsome',
plugins: [
{
use: 'gridsome-source-storyblok',
options: {
client: {
accessToken: '<YOUR_ACCESS_TOKEN>'
},
types: {
story: {
typeName: 'StoryblokEntry'
}
}
}
}
]
}
- In the
src/templates
folder create a .vue
file with the same name you defined in the typeName
option (default is StoryblokEntry
). After that set a <page-query>
tag to load the data from GraphQL. For example:
<page-query>
query StoryblokEntry ($id: ID) {
storyblokEntry (id: $id) {
id
slug
content
}
}
</page-query>
- Edit the file
gridsome.server.js
to use a GraphQL query to generate the pages using Storyblok's full_slug attribute
module.exports = function (api) {
api.createPages(async ({ graphql, createPage }) => {
const { data } = await graphql(`{
allStoryblokEntry {
edges {
node {
id
full_slug
}
}
}
}`)
data.allStoryblokEntry.edges.forEach(({ node }) => {
createPage({
path: `/${node.full_slug}`,
component: './src/templates/StoryblokEntry.vue',
context: {
id: node.id
}
})
})
})
}
The options object in details
When you declare the use of the Storyblok plugin you can pass following options:
{
use: 'gridsome-source-storyblok',
options: {
client: {
accessToken: '<YOUR_ACCESS_TOKEN>'
},
version: 'draft',
types: {
story: {
name: 'StoryblokEntry',
params: {}
},
tag: {
name: 'StoryblokTag',
params: {}
}
},
downloadImages: true,
imageDirectory: 'storyblok_images',
additionalTypes: [
{
type: 'datasources',
name: 'StoryblokDatasource'
},
{
type: 'datasource_entries',
name: 'StoryblokDatasourceEntry',
params: { ...additionalQueryParams }
},
{
type: 'links',
name: 'StoryblokLink'
}
]
}
}
Rendering of the Rich Text field
This plugin comes with a built in renderer to get html output from Storyblok's Rich Text field. Create and register a Richtext.vue
component with the code below to use the renderer in your components like this: <richtext :text="blok.richtext"></richtext>
.
<template>
<div>
<div v-html="richtext"></div>
</div>
</template>
<script>
export default {
props: ['text'],
computed: {
richtext() {
return this.text ? this.$storyapi.richTextResolver.render(this.text) : ''
}
}
}
</script>
Downloading images
When downloadImages
option is marked as true, this plugin will be searching in each story for a image and download it to src/<imageDirectory>
folder. In your components, you can use the g-image tag. An important thing is that image property in story will be a object with some fields, not a string. Bellow, we show you an example of this:
<template>
<div>
<g-image :src="imageURL"></g-image>
</div>
</template>
<script>
export default {
props: ['blok'],
computed: {
imageURL () {
if (typeof this.blok.image === 'string') {
return this.blok.image
}
const path = this.blok.image.path
return require('!!assets-loader?width=800&quality=100&fit=inside!~/' + path)
}
}
}
</script>
<style scoped>
img {
max-width: 100%;
}
</style>
Working with Tags
By default, this plugin will get all tags and create a reference to stories entries (as described in create-schema
function), so it's possible to list stories from tag, for example.
You can change the name of template file and types by setting the options.types.tag.name
option in gridsome.config.js
(StoryblokTag
is default).
Example
Create a StoryblokTag.vue
file in src/templates
folder with the following code:
<template>
<Layout>
<h1>{{ $page.storyblokTag.name }}</h1>
<ul>
<li v-for="edge in $page.storyblokTag.belongsTo.edges" :key="edge.node.id">
<g-link :to="edge.node.full_slug">
{{ edge.node.name }}
</g-link>
</li>
</ul>
</Layout>
</template>
<page-query>
query ($id: ID!) {
storyblokTag(id: $id) {
name
belongsTo {
edges {
node {
... on StoryblokEntry {
id
full_slug
name
}
}
}
}
}
}
</page-query>
In your gridsome.server.js
file, it will be necessary to create a pages for each tag as the following:
module.exports = function (api) {
api.createPages(async ({ graphql, createPage }) => {
const { data: tagData } = await graphql(`{
allStoryblokTag {
edges {
node {
id
name
}
}
}
}`)
tagData.allStoryblokTag.edges.forEach(({ node }) => {
createPage({
path: `/tag/${node.name}`,
component: './src/templates/StoryblokTag.vue',
context: {
id: node.id
}
})
})
})
})
That's all! In your browser you can view a list of stories by the foo
tag in http://localhost:8080/tag/foo
.
Load data to different collections
To load data to multiple collections, you need to declare the configuration multiple times in gridsome.config.js
. Like this:
{
siteName: 'Gridsome',
plugins: [
{
use: 'gridsome-source-storyblok',
options: {
client: {
accessToken: '<YOUR_ACCESS_TOKEN>'
}
}
},
{
use: 'gridsome-source-storyblok',
options: {
client: {
accessToken: '<YOUR_ACCESS_TOKEN>'
},
types: {
story: {
name: 'StoryblokBlogEntry',
params: {
starts_with: 'blog/'
}
},
tag: {
typeName: 'StoryblokBlogTag'
}
}
}
}
]
}
And, in your gridsome.server.js
, you can generate your pages for each collection, attending to the name given to each collection.
Get Space informations
It is possible to get the space informations using the GraphQL Data Layer. The space information will be storage in Gridsome's global metadata, so, it will be avaialable for your entire project.
To get the space informations, you can set this following query in your <static-query>
in your vue component:
query {
metadata {
storyblokSpace {
id
name
version
language_codes
}
}
}
Contribution
Fork me on Github.
This project use semantic-release for generate new versions by using commit messages and we use the Angular Convention to naming the commits. Check this question about it in semantic-release FAQ.