Storyblok
Search Storyblok's Documentation
  1. Visual Editor

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. 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 type
    • space: the ID of the space
    • uid: the UID of the block
    • id: 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. 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.

  3. 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 or publish events in the editor. You can customize and fine-grain this behavior (including rendering updates instantly on input) with a manual implementation of the StoryblokBridge 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 and publish events configured, the menu will display navigation actions. With the input 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. 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.