
Surjit Bharath
Director of Hidden Foundry
Optimizely Most Valued Professional (OMVP), Subject Matter Expert (SME), CMS and Commerce certified
Contact MeIn this article, we’ll walk through how to build a Storyblok-powered blog that supports both preview and production environments using the same codebase. You’ll learn how to request different versions of content, see what changes between them, and understand how your frontend can cleanly handle both without needing code changes between environments.
Requesting Different Versions of Content
The most direct way to understand how Storyblok handles versioning is by looking at a typical content API request.
GET https://api.storyblok.com/v2/cdn/stories/{slug}?version={draft|published}&token={your_token}
Let’s break that down:
-
The slug (e.g.
aeronauts
) identifies the piece of content you’re fetching. -
The version parameter controls which state of the content you want:
-
draft
returns the latest saved edits (even if they’re not published). -
published
returns the last publicly published version.
-
-
The token determines access:
-
The preview token can fetch both draft and published versions.
-
The public token can only fetch published content.
-
Key takeaway: The only difference between a preview and production site is the combination of token and version used in the request.
Understanding the Response
A successful request returns a JSON object containing a story
. That object holds both metadata and the custom content fields you've defined in your components.
Here’s a simplified example of a published response:
{
"story": {
"name": "My Article",
"content": {
"component": "blog_post",
"title": "Aeronauts: A Journey",
"body": "This is the article body"
},
"published_at": "2025-06-25T12:00:00Z",
"uuid": "abcd-1234"
}
}
In this response, the story.content
object contains all your schema fields such as the title, body, and the component type. The published_at
field shows when the content was last published, and the uuid
uniquely identifies this piece of content.
Now, if you fetch the same slug using version=draft
and someone has saved changes without publishing, you’ll get a response like this:
{
"story": {
"content": {
"title": "Aeronauts: A Bold New Take"
}
}
}
That confirms what we expect: the version
parameter controls whether you receive published content or work-in-progress draft content using the same URL structure.
Fetching Versions in Code
If you're using Vue with the official Storyblok SDK, you can programmatically request different versions like this:
Setup
First, install the Storyblok SDK:
npm install @storyblok/vue
Then, initialize it in your main file (e.g. main.ts
or main.js
):
import { StoryblokVue, apiPlugin } from '@storyblok/vue'
app.use(StoryblokVue, {
accessToken: import.meta.env.VITE_STORYBLOK_TOKEN,
use: [apiPlugin]
})
๐ You can control the version (
draft
orpublished
) using an environment variable as well, e.g.VITE_STORYBLOK_VERSION
.
Fetching a Single Story (e.g. in a page component)
import { useStoryblokApi } from '@storyblok/vue'
const storyblokApi = useStoryblokApi()
const { data } = await storyblokApi.get('cdn/stories/aeronauts', {
version: import.meta.env.VITE_STORYBLOK_VERSION
})
This request will return the correct version of the aeronauts
story based on your build-time environment configuration.
Fetching Multiple Stories (e.g. for a blog listing)
const { data } = await storyblokApi.get('cdn/stories', {
starts_with: 'blog/',
version: import.meta.env.VITE_STORYBLOK_VERSION
})
This will return all stories under the
blog/
prefix using either the draft
or published
version depending on your setup.By controlling the token and version through environment variables (
.env
for local/dev and platform config for deployed environments), you can cleanly support both preview and production workflows without any code changes between environments.
Summary
Storyblok makes versioning straightforward: content exists in two states draft and published and you control which one you retrieve using a query parameter and a token.
This setup enables you to support real-time previews and safe production deployments from a single codebase. Whether you're building a blog, a marketing site, or something more complex, this model gives you and your content editors a clean, reliable workflow.