Add a headless CMS with Live Preview to SvelteKit in 5 Minutes

    Try Storyblok

    Storyblok is the first headless CMS that works for developers & marketers alike.


    Please note that this article has already been updated to match Storyblok V2. If you haven’t already started using it, you can find out how to make the switch here.

    This short tutorial will show you how to integrate the Storyblok API into a SvelteKit app. We will also enable the real-time editing experience in the Visual Editor with the Storyblok JS Bridge - so that you can see your changes right away.  At the end of this article, you will have a SvelteKit application that renders components filled with data from Storyblok. 

    If you don't know what a Headless CMS is or what Storyblok does, please read first the Storyblok Guide.

    Live Demo:

    If you’re in a hurry, try the live demo in Stackblitz!

    Environment Setup


    To follow this tutorial, make sure to meet these requirements:

    Create a Sveltekit project

    Let’s create a Sveltekit project following the official installation guide

    npm init svelte@next my-app 
    cd my-app
    npm install
    npm run dev

    When creating your new project, you will be asked if you want to set up some basic tooling like TypeScript. For the purpose of our project, you can select the basic skeleton project without Typescript and Playwright browser testing. We also don't require Eslint or Prettier, but feel free to set these things up according to your needs. 


    There is a Sveltekit Plugin for VS Code that helps with syntax highlighting.

    You should now be able to see the SvelteKit App in your browser when you open http://localhost:5173/:

    Start screen of SvelteKit App


    By default, SvelteKit runs on port 5173 - if you’d like to change that, Rodney created a great tutorial on changing the SvelteKit Port.

    Setting up a HTTPS Connection

    V2 works with a secure connection through HTTPS. To set this up with our SvelteKit project, we will install the mkcert plugin like so:

    npm i vite-plugin-mkcert -D

    Then, in our vite.config.js, let's import mkcert and set the https to true:

    import { sveltekit } from '@sveltejs/kit/vite';
    import mkcert from 'vite-plugin-mkcert'
    /** @type {import('vite').UserConfig} */
    const config = {
        plugins: [sveltekit(), mkcert()],
        server: {
            https: true
    export default config;

    Configuration of the space

    Create a new space in the Storyblok app by clicking + Create new space and choosing the Create new space {1} option. Pick a name for it {2}.
    Creating a new space in the storyblok app

    Now, a Storyblok space with sample content has been created. If you open the Home story you will see the Visual Editor on your screen:
    Welcome screen in the visual editor

    Understand what represents Story in our Storyblok Guide.

    Enabling the Visual Editor 

    To see your website in the Storyblok Visual Editor you need to set the default environment URL. For that, in your space, open Settings > Visual Editor {1} and set the Location field to https://localhost:5173/ {2} and save {3} .
    Configuring default environment to localhost

    Now go back to the Home story under the Content section. You'll see the Visual Editor when you open it, but you won't see your SvelteKit application yet. 

    Now, open the Config tab {1} on the right-hand form, and set the Real Path to “/” {2}. Save, and if you’re still running your SvelteKit app, you will see it now in the Visual Editor.
    Sveltekit Start screen in the Visual Editor

    Connecting SvelteKit to Storyblok

    First of all, let’s install @storyblok/svelte, our official SDK for all things Svelte & SvelteKit: 

    npm install @storyblok/svelte

    It allows you to interact with Storyblok API and enable the real-time editing experience. Let's configure it.

    First of all, you need to grab your access token from your space Settings > Access Token:
    Adding the access token

    Now let’s create a __layout.svelte file in the src/routes folder of our SvelteKit app. Here we can initialize the library, add the ApiPlugin and the access token of our Storyblok space: 

    <script context="module">
        import { storyblokInit, apiPlugin } from "@storyblok/svelte";
          accessToken: "your-preview-token",
          use: [apiPlugin]

    storyblokInit sets up the connection with the space. It initializes the Storyblok Bridge, that allows us to enable the real-time editing experience inside the Visual Editor. The function also provides an instance of the Storyblok API client that we can use to retrieve content from Storyblok.

    If you are planning to use the Storyblok API client to call the REST API, make sure to include the ApiPlugin when initializing the storyblokInit() function.


    Creating SvelteKit Components

    The core idea of using Storyblok for this particular use case is the following:

    • Content managers (even if it’s only yourself) can create pages (or stories) composed of different components (or bloks)

    • Developers receive the page in the JSON format by using the Storyblok API and can render components accordingly (this is what we want to accomplish in our SvelteKit app)

    When you create a new space from scratch, Storyblok automatically creates four default components for you:

    • page (content type)

    • grid (nested component)

    • feature (nested component)

    • teaser (nested component)

    You can find all of these in the Components section of your space.


    Understand the difference between the nested components and content type in our Structures of Content tutorial.

    Optional: Use TailwindCSS to style your components

    If you had a sneak peek at our Stackblitz demo, you might have noticed that we’ve been using Tailwind classes in our components. In order to make these work, let’s quickly add Tailwind to our SvelteKit project with svelte-add by running:

    npx svelte-add tailwindcss
    npm install

    This should first of all import svelte-preprocess and configure it to process <style> blocks as PostCSS. Make sure preprocess is imported in your `svelte.config.js` and `postcss` set to 'true', like so:

    import preprocess from "svelte-preprocess";
    import adapter from "@sveltejs/adapter-auto";
    /** @type {import('@sveltejs/kit').Config} */
    const config = {
      kit: {
        adapter: adapter(),
      preprocess: [
          postcss: true,
    export default config;

    Adding Tailwind to your project will also add a `tailwind.config.js` including all the template files (no need to make any changes here):  

    const config = {
      content: ["./src/**/*.{html,js,svelte,ts}"]
    module.exports = config;

    And a postcss.config.cjs (again, no need to make changes):

    const tailwindcss = require("tailwindcss");
    const autoprefixer = require("autoprefixer");
    const config = {
      plugins: [
        //Some plugins, like tailwindcss/nesting, need to run before Tailwind,
        //But others, like autoprefixer, need to run after,
    module.exports = config;

    To render your styles successfully, make sure to import the `app.css` in your __layout.svelte like so:

        import "../app.css";

    To learn more, you can also checkout the official docs on adding Tailwind to a SvelteKit project.

    Adding Components

    As you may have already seen, the content in Storyblok is structured as components (or blocks). We can have stories, that are composed of different components. You can see an example in the Home story of our new space.


    If you want to learn more about how the content is structured in Storyblok, you can read this guide.

    In our example on Stackblitz, we have created 4 example components in a new src/components folder: 

    • Feature

    • Grid

    • Page

    • Teaser

    To make your components editable in the Storyblok Visual Editor, you have to add the use:storyblokEditable={blok} action to the root of each component that you are loading in your storyblokInit. For example, in Teaser.svelte:

    import { storyblokEditable } from "@storyblok/svelte";
    export let blok;
    <div use:storyblokEditable={blok} class="py-8 mb-6 text-5xl font-bold text-center">
        { blok.headline }

    Use the StoryblokComponent to load them by passing the blok property.


    To be able to make the most of the Visual Editor and JS Bridge and display all changes in real-time, in Svelte, we have to wrap our page component in a {#key} block. This will destroy and recreate the contents every time the value changes. This way, we will be able to drag and drop our components and see the changes on the fly. Find out more about the key blok in the Svelte docs or have a look at our Stackblitz example for the implementation.

    Now, add all the components that you want to link to Storyblok in the components object in the storyblokInit() function in __layout.svelte. This will link them to their representation in the Storyblok space. You can load all of them at the same time by adding them to the list, like so:

    <script context="module">
      import { storyblokInit, apiPlugin } from "@storyblok/svelte";
      import Feature from '../components/Feature.svelte';
      import Grid from '../components/Grid.svelte';
      import Page from '../components/Page.svelte';
      import Teaser from '../components/Teaser.svelte';
        accessToken: "your-preview-token",
        use: [apiPlugin],
        components: {
          feature: Feature,
          grid: Grid,
          page: Page,
          teaser: Teaser,

    The blok is the actual blok data coming from Storyblok’s Content Delivery API.

    Fetching the Content 

    Now you can use the useStoryblokApi() in the index.svelte to get your stories from the Storyblok CDN API: 

    <script context="module">
      import { useStoryblokApi } from "@storyblok/svelte";
      export async function load(){
        const storyblokApi = useStoryblokApi();
        const { data } = await storyblokApi.get("cdn/stories/home", { version: "draft",
        return {
          props:{ story: data.story } 

    Listening to changes in the Visual Editor

    To enable the life editing experience, you can use the useStoryblokBridge() function provided from @storyblok/svelte in your index.svelte file.

      import { onMount } from "svelte";
      import {useStoryblokBridge, StoryblokComponent } from "@storyblok/svelte";
      export let story;
      onMount(() => {
        useStoryblokBridge(, (newStory) => (story = newStory));
      {#if story}
        <StoryblokComponent blok={story.content} />

    And that’s it. When you now check out your story in the Visual Editor, the Storyblok JS Bridge is enabled, and you can make real-time changes:
    Visual Editor with JS Bridge Enabled

    Wrapping Up

    Congratulations! You now have a SvelteKit app with dynamic components, dynamic pages and complete integration with Storyblok, providing a unique real-time editing experience.

    Next Part:

    Continue reading and find out How to Render Storyblok Stories Dynamically in SvelteKit.