Tool Plugin
Tools are a different way to extend your visual editor. They allow you to improve your productivity by adding some functionality to the editor. Two examples of Tools are importing and exporting content or a custom clipboard.
Tools are specific to stories and will appear in your Visual Editor when editing a story. You can access the installed tools from the top bar
{1}. Here we have installed the Import Translatable Fields {2}.

Getting Started
To create a new tool you need to be signed up as a Partner. Head into the partner portal and click on Apps {1} and then the New App
button {2}. As App type select Tool {3} and click Save {4}.

Create New Tool
To get started we will clone the tool plugin starter template: github.com/storyblok/storyblok-tool-example
$ git clone https://github.com/storyblok/storyblok-tool-example.git
$ cd storyblok-tool-example
$ npm install
You will need to add the tunnel URL to the development settings in your tool settings. Open your tool and go to Oauth 2
{1} You need a URL to the app, e.g. the tunnel URL + auth/connect/storyblok
{4} and an Oauth2 callback, e.g. the tunnel URL + auth/callback
{5}.
We will need to install ngrok to create a tunnel to our local application. Once you installed ngrok, you can start the tunnel to port 3000.
npm i -g ngrok
ngrok http 3000

Tool Settings
Inside the starter, you will find a .env-example
file. Rename this file to .env
and fill in the client id {2} and client secret {3} as well as the Oauth2 callback {5}.
CONFIDENTIAL_CLIENT_ID="Id from Storyblok App"
CONFIDENTIAL_CLIENT_SECRET="Secret from Storyblok App"
CONFIDENTIAL_CLIENT_REDIRECT_URI=https://YOUR_ID.ngrok.io/auth/callback
Now we can start our development server:
npm run dev
Now if you open http://localhost:3000/
it should redirect to Storyblok, but nothing happens. In order to test it, we first need to install our tool plugin in a specific space. To install the plugin go to the General
tab {1} and copy the Install Link
{2} and open it in a new tab.

General Settings
You will see a screen to install the app. Here you can select the space {1} you want to install the tool in. After selecting the space, click on the Install app
button {2}

Installing the Tool
After installation, you will be redirected to the App Directory
of the space for which you installed the tool and you will be able to see the tool under the Installed
section.
Now, go to the content section and open a story.
Make sure to append ?dev=1
in the end of your url like app.storyblok.com/#!/me/spaces/1/apps/1/detail?dev=1
to open the app with the development URL.
Inside the story, go to the Tools
section {1}. The first time you should see a confirmation screen inside the installed tool window to authorize the application to have access to Storyblok. Click Approve {2}.

Approving the tool
Once you authenticate your tool you should be able to see it working.

Tool in action
Reading the Story Context
Inside a tool, you can use the context to get the data of the current story. For that you have to use two window events: message
and postMessage
.
First, we let Storyblok know that there is a tool via the following event. Make sure that name of the tool you created matches the name in the tool
parameter.
window.parent.postMessage({
action: 'tool-changed',
tool: 'storyblok-gmbh@my-test-tool',
event: 'getContext'
}, 'https://app.storyblok.com')
After that Storyblok will reply with the Story context, so we need to listen for that with the following listener:
window.addEventListener('message', this.processMessage, false)
Finally, we need to connect everything and add the processMessage
method to set our local component data:
export default {
data() {
return {
story: {},
loadingContext: true
}
},
mounted() {
// redirects you to Storyblok
if (window.top === window.self) {
window.location.assign('https://app.storyblok.com/oauth/tool_redirect')
}
// listens for Storyblok answer
window.addEventListener('message', this.processMessage, false)
// Use getContext to get the current story
window.parent.postMessage({action: 'tool-changed', tool: 'storyblok-gmbh@my-test-tool', event: 'getContext'}, 'https://app.storyblok.com')
},
methods: {
processMessage(event) {
// processes the event answer
if (event.data && event.data.action == 'get-context') {
this.loadingContext = false
// get the context story
this.story = event.data.story
// get the context language
this.language = event.data.language
}
}
}
}
Changing the Height of the Tool
It's possible to change the height of the tool by sending an event with the name heightChange
with a height
parameter to the parent window
window.parent.postMessage({
action: 'tool-changed',
tool: 'storyblok-gmbh@my-test-tool',
event: 'heightChange',
height: 500},
'https://app.storyblok.com')
Reading the Editor Language
It's possible to get the current language from the get-context
event.
export default {
mounted() {
window.addEventListener('message', this.processMessage, false)
window.parent.postMessage({action: 'tool-changed', tool: 'storyblok-gmbh@my-test-tool', event: 'getContext'}, 'https://app.storyblok.com')
},
methods: {
processMessage(event) {
if (event.data && event.data.action == 'get-context') {
this.language = event.data.language
}
}
}
}
Changing Content via the API
Tools work with the storyblok-nuxt-auth module to retrieve and change data from Storyblok via the Storyblok Management API. Inside of a tool you can get for example the user information with the following request:
axios.get('/auth/user', {params: {space_id: this.$route.query.space_id}})
.then((response) => {
console.log(response.data)
})
You can also change stories inside of Storyblok by making post or put requests. You can create for example a new story with the following code:
import axios from 'axios'
export default {
data() {
return {
loading: false,
story: {
name: ''
}
}
},
methods: {
createStory() {
this.loading = true
// The request body is the same from Management API
// https://www.storyblok.com/docs/api/management#core-resources/stories/create-story
const body = {
story: { ...this.story }
}
// get the space id from URL and use it in requests
return axios
.post(`/auth/spaces/${this.$route.query.space_id}/stories`, body)
.then((res) => {
this.loading = false
})
}
}
}