The Storyblok Bridge

To build a bridge between Storyblok and your website, there needs to be included a script on your page. This script will communicate via iframe with Storyblok to tell the editing interface which component needs to be opened when the user clicks on it.

This is done using a simple HTML comment before that element which is shipped in the draft JSON by adding the key _editable to each component.

Include the script inside your <body> tag

<script src="//" type="text/javascript">

You can see the preview token (= YOUR_TOKEN) via the dashboard of your space. You can also conditionally exclude the script tag if the user is not in the edit mode.

Methods to check for the edit mode:

  • Check for the URL parameter _storyblok
  • Check for a cookie (You could set a cookie the first time the url has _storyblok in it)


For a server rendered website the following code will reload your website as soon the user saves or publishes a story.

// Optionally init if you have not provided the ?t parameter to the script tag already.
  accessToken: 'your_token'

storyblok.on(['published', 'change'], function() {

storyblok.pingEditor(function() {
  if (storyblok.inEditor) {


You can listen to events calling the on handler:

storyblok.on('event_name', function(data) {

For the published event this would look like:

storyblok.on('published', function(data) {
  // invalidate cache, ...

For the app “Sync Button” there is a special event you can listen to:

storyblok.on('customEvent', function(data) {
  if (data.event == 'start-sync') {
    // Do sync

You get a 404 error after changing the slug of a Story?
If you use Storyblok with serverside rendering and want to reload whenever the content changed you need to make an additional check on the slugChanged attribute. This makes sure that the page doesn’t throw a 404 error if you change the slug:

storyblok.on(['published', 'change'], function(event) {
  if (!event.slugChanged) {
EventWill be emitted
inputafter a user changes the value of a field
changeafter the user saves the content
publishedafter the user clicks publish
unpublishedafter the user clicks unpublish
enterEditmodeafter the editor has been initialized in the editmode
customEventcustom event used by third party plugins



config Object

  • accessToken String: The private token of the Content Delivery API to receive draft versions of content. You can find it in your space dashboard at
  • customParent String: If you are using the Storyblok editor on your own domain a url needs to be provided. Default is:


  accessToken: 'xf5dRMMjltLzKOcNgMaU9Att'

storyblok.isInEditor() and pingEditor()

With these functions you can check if your user has opened a page inside Storyblok.


storyblok.pingEditor(() => {
  if (storyblok.isInEditor()) {
    // getStory('draft')
  } else {
    // getStory('published')

storyblok.get(query, success, error)

Get a single content entry in the frontend.

  • query Object
    • version String: ‘draft’ or ‘published’. Default is ‘published’
  • success Function: Success callback
  • error Function: Error callback


  slug: 'home', 
  version: 'draft'
}, (data) => {
  var story = data.story

storyblok.getAll(query, success, error)

Get multiple content entries

  • query Object
    • with_tag, String
    • starts_with, String
    • version, String
    • sort_by, String
    • filter_by, String
    • per_page, String
    • page, String
  • success Function, Success callback
  • error Function, Error callback


  version: 'draft'
}, (data, xhrResponse) => {
  var total = xhrResponse.getResponseHeader('Total')
  var stories = data.stories


Why is the live preview not working?

Check if you are not redirecting to another url by clicking “Open draft” in the top right submenu. The url should contain the query parameter _storyblok.

How to validate if the user is viewing your site in the Storyblok editor?

If the user opens your page in Storyblok, we add a few parameters which you can use to securely validate their use of the edit mode.

You will need that validation to load the right version of your content to the right users. The draft version is for the editor and the published version is for the public.

A simple validation would be to check if there is the _storyblok parameter in url. This could be done in the frontend or in the backend. But for a secure check we recommend to implement the logic in the backend and validate the _storyblok_tk parameter.

Here are some examples of how to securely check if the user is in edit mode:

import crypto from 'crypto'
// getQueryParam = access requests URL param by name

let validationString = getQueryParam('_storyblok_tk[space_id]') + ':' + YOUR_PREVIEW_TOKEN + ':' + getQueryParam('_storyblok_tk[timestamp]')
let validationToken = crypto.createHash('sha1').update(validationString).digest('hex')
if (getQueryParam('_storyblok_tk[token]') == validationToken && 
    getQueryParam('_storyblok_tk[timestamp]') > Math.floor( { 
    // you're in the edit mode.
    this.editMode = true 
$sb = $app['request']->query->get('_storyblok_tk');
if (!empty($sb)) {
    $pre_token = $sb['space_id'] . ':' . YOUR_PREVIEW_TOKEN . ':' . $sb['timestamp'];
    $token = sha1($pre_token);
    if ($token == $sb['token'] && (int)$sb['timestamp'] > strtotime('now') - 3600) {
        $app['config.storyblok.edit_mode'] = true;
        // you're in the edit mode.

These are the parameters Storyblok appends to your url:

_storyblokthe content entries ID
_storyblok_tk[space_id]the space ID
_storyblok_tk[timestamp]a timestamp
_storyblok_tk[token]a validation token. Combination of space_id, timestamp and your preview_token
_storyblok_releasecurrently selected id of a release
_storyblok_langcurrently selected language
_storyblok_ccontent type of the story