Visual Editor
Most CMSs force their users to make a compromise between integration and customization. A seamless integration with the CMS usually means that the user has less freedom to customize their website or vice versa.
Storyblok offers the best of both worlds: a WYSIWYG editing experience embedded directly in the Storyblok Visual Editor with full freedom to customize the website's back and front end.
The visual editing experience enables natural interactions:
- Clicking on a block in the editor will scroll the corresponding element in to the preview viewport, and clicking on an element in the preview will open the block in the editor
- Outlines appear around block elements in the previewed website
- Context menus appear on elements in the preview website, aiding with editing and navigation
- Edits appear in the preview on change in the editor (or as defined by the website developer)
Together, these features create an intuitive editing experience for everyone on your team — from sales reps to HR managers to developers to marketers.
To facilitate a seamless editing experience, Storyblok offers a simple framework based on four steps:
- 1
Fetch draft content
To render a preview, configure your website to fetch the draft version of your content in your preview website. Learn more about creating a preview deployment.
import { apiPlugin, storyblokInit } from '@storyblok/js'; const { storyblokApi } = storyblokInit({ accessToken: 'YOUR_ACCESS_TOKEN', use: [apiPlugin], }); const response = await storyblokApi.get('cdn/stories/home', { version: 'draft' } )
This will ensure that your preview fetches the draft version of your content when it appears in the editor. It will also provide important data to enable live editing.
The draft version of each story’s API response includes a private
_editable
property on each block, which looks like this:"body": [ { ... }, "component": "page", "_editable": "\u003C!--#storyblok ... --\u003E" // Shortened for space }
These
_editable
properties function like a secret code that tells the Visual Editor how to handle the elements on your front end.Inside the
_editable
property is a string of encoded JSON. When that JSON is extracted and parsed, it has four properties:name
: the name of the block typespace
: the ID of the spaceuid
: the UID of the blockid
: the ID of the story
To expose this information to the Visual Editor, you will add it to your HTML, as described in the next step.
- 2
Add HTML attributes
Each block in your website should have two attributes:
- A
data-blok-c
attribute that contains the stringified JSON from the_editable
property - A
data-blok-uid
attribute that contains the ID and UID extracted from the block’s_editable
property, in the pattern${id}-${uid}
The Storyblok SDKs include utilities to format the data attributes:
const attributes = storyblokEditable(blok) const element = ` <div class="storyblok__outline" data-blok-c="${attributes["data-blok-c"]}" data-blok-uid="${attributes["data-blok-uid"]}" > <!-- Content --> </div> `
Your front end will need one more element in order to connect to the Visual Editor: the bridge script.
- A
- 3
Run bridge script
The bridge script handles most of the heavy lifting required for live editing. Storyblok hosts the script on a dedicated CDN:
https://app.storyblok.com/f/storyblok-v2-latest.js
You can either load the script manually or use one of Storyblok’s front-end SDKs, such as
@storyblok/js
:import { apiPlugin, storyblokInit, useStoryblokBridge } from '@storyblok/js'; const { storyblokApi } = storyblokInit({ accessToken: 'YOUR_ACCESS_TOKEN', use: [apiPlugin], }); const { data } = await storyblokApi.get('cdn/stories/home', { version: 'draft' } ) // Activate StoryblokBridge useStoryblokBridge(data.story.id)
Once activated, the Storyblok Bridge will reload the webpage upon
save
orpublish
events in the editor. You can customize and fine-grain this behavior (including rendering updates instantly on input) with a manual implementation of theStoryblokBridge
class. See the StoryblokBridge reference for further information.Writing a custom bridge configuration is a great way to create a dedicated editing experience for your team. Here are some tips to make sure everything goes smoothly:
When you activate the Storyblok Bridge, resolve any links and relations that you have resolved in the API request to ensure the linked data populates everywhere.
If you are writing custom logic for your bridge events, handle slug changes, which could cause a 404 when the page refreshes
If a page has events configured in the bridge, a context menu will appear on block elements on the page. With
save
andpublish
events configured, the menu will display navigation actions. With theinput
event configured, the menu will also display editing actions.With these steps completed, your application is ready to communicate with the Storyblok Visual Editor. For the next step, you’ll wire up the Visual Editor.
- 4
Configure URLs
The Visual Editor loads your webpage in an iframe. Enter your website domain by clicking Add or change preview URLs from within the Visual Editor.
The website that you preview must load via HTTPS.
The website that you serve in the Visual Editor can be a dedicated preview deployment. The preview environment should build from the same codebase as your production deployment and load preview functionality based on environment variables. Learn more in our tutorial on creating a preview environment.
When the Visual Editor loads your website, it will build a URL from your base domain (
example.com
) and the story’s full slug (blog/example-category/example-uid
).The Visual Editor will also append seven parameters to the URL:
_storyblok
:The numeric story ID_storyblok_tk[space_id]
: The space ID_storyblok_tk[timestamp]
: A UNIX timestamp_storyblok_tk[token]
: A validation token (a combination of_storyblok_tk[space_id]
,_storyblok_tk[timestamp]
, and the preview access token)_storyblok_release
: ID of the current release_storyblok_lang
: ID of the current language_storyblok_c
: The content type of the story
Here is an example:
https://your-preview-environment.com/folder-a/landing-page?_storyblok=580906535&_storyblok_c=page&_storyblok_version=&_storyblok_lang=default&_storyblok_release=0&_storyblok_rl=1732540047643&_storyblok_tk[space_id]=313862&_storyblok_tk[timestamp]=1732540047&_storyblok_tk[token]=9d25c03de1478da57e37a166a7c053ce0aff9234
If the path to your webpage is different from the associated story’s slug, you can define a Real path in the Visual Editor. The real path will not change the story’s endpoint or the story’s slug.