⚠️Draft Content

Storyblok Raises $80M Series C - Read News

What’s the True Total Price of Enterprise CMS? Find out here.

Skip to main content

How to Create Preview & Production Environments and Deploy Your Website

Try Storyblok

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

Creating preview and production environments and deploying websites are essential, allowing your content editors to switch between draft and published views of the content. This tutorial will guide you through setting up optimized preview and production environments and deploying your website from a technology-agnostic perspective.

There are two main ways to deploy Storyblok-driven websites: using one environment for preview and production or separate, dedicated environments for preview and production. Both approaches allow developers and content editors to see the different versions of the content in real-time easily.

Moreover, both have benefits and drawbacks, and several factors should be considered when deciding on your ideal deployment strategy.

One combined environment

Using one environment for preview and production simplifies the deployment process, as only one project has to be managed on the hosting service.

  • Pros: Everything can be managed in one repository and one project on the hosting service.
  • Cons: A single environment can become more complicated and require additional code, depending on the technology or framework.

Two separate environments

Using separate, dedicated environments for preview and production provides the flexibility to test code changes in a safe environment to preview them and only deploy the approved changes into production.

Moreover, this approach allows you to configure different rendering modes per environment easily. For instance, you may want to deploy your production build using Static Site Generation (SSG) to optimize performance and utilize Server Side Rendering (SSR) on your preview build to offer the benefits of real-time editing to your editors.

Finally, two separate environments allow you to test code changes before deploying them to production.

  • Pros: Clear separation of concerns; easy configuration of rendering modes depending on the context; potential performance benefits; possibility to test code changes in the preview environment before deploying to production.
  • Cons: Two projects have to be maintained on the hosting service; conditional statements to differentiate between the two environments have to be implemented in the code base.

This approach has been widely adopted because of its advantages and is our usual recommendation.

Setting up two dedicated environments

First, let's make sure you have Storyblok space ready and have chosen a hosting provider that supports your framework (e.g., Netlify, Vercel, Cloudflare Pages, etc.). Once logged in at your hosting service, you will want to create two projects using the same git repository: one constitutes your production environment, and the other constitutes your preview environment. Each of these will have a dedicated URL, which you can configure in Storyblok to allow content editors to switch between the two environments right from within the Visual Editor. To accomplish that, in Storyblok, let's navigate to Settings > Visual Editor. As the default environment, you will typically want to define the URL of the deployed preview environment, as this allows your editors to preview content changes in real time. In the Preview URLs section, you can add the URL of the deployed production environment.

The easiest way to differentiate between production and preview in your code is to utilize environment variables. These allow us to create conditional statements in order to determine whether the draft or published content should be fetched, which rendering mode should be employed, and more. For our purposes, we'll rely on two environment variables, STORYBLOK_TOKEN and STORYBLOK_IS_PREVIEW, which need to be configured in three places: the production environment, the preview environment, and the local environment.

For the production environment, let's define them as follows:

Production environment variables
        
      STORYBLOK_TOKEN=your-public-access-token
STORYBLOK_IS_PREVIEW=false
    

For the preview environment, in contrast, let's define these values:

Preview environment variables
        
      STORYBLOK_TOKEN=your-preview-access-token
STORYBLOK_IS_PREVIEW=true
    

hint:

Most hosting services automatically add context-based environment variables, such as the site ID, name, or URL. Instead of defining STORYBLOK_IS_PREVIEW manually, you may find one of these suitable for your purposes, too.

Having defined these variables, they can now be used to differentiate clearly between production and preview, conditionally enabling key functionality. Most importantly, we'll need to enable Storyblok's real-time editing feature in the preview environment. For this to work, the following four criteria have to be fulfilled:

  1. The draft version of the content needs to be fetched
  2. The Storyblok Bridge needs to be enabled
  3. The preview access token has to be used
  4. A compatible rendering mode needs to be enabled

Let's take a look at all of these individually.

Loading the correct content version

For this purpose, it makes sense to create a convenient utility function that returns the correct version depending on the context. As you will most likely fetch content from the Storyblok Content Delivery API in several places (e.g., a dynamic catch-all route, sitemap generation, fetching the latest blog articles), it is helpful to centralize the logic in one place, ensuring that the proper version is loaded everywhere and improving maintainability. Such a helper function could look similar to this:

utils/getVersion.js
        
      export default function getVersion() {
  return process.env.STORYBLOK_IS_PREVIEW === 'true' ? 'draft' : 'published'
}
    

This function can then be used in all of your API requests. In the case of a catch-all route, it could look similar to this:

Using the getVersion() utility function
        
      
const { data } = await storyblokApi.get(
  `cdn/stories/${slug === undefined ? 'home' : slug}`,
  {
    version: getSbVersion(),
    resolve_links: 'url',
  }
)
    

Conditionally enabling the Storyblok Bridge

When using any of Storyblok's framework SDKs, the Bridge is typically enabled or disabled in the same location where the SDK is initialized and configured. Please take a look at the documentation for the framework SDK of choice. Using the STORYBLOK_IS_PREVIEW environment variable, the bridge can easily be enabled conditionally as follows:

Conditionally enabling the Storyblok Bridge
        
      storyblokInit({
  bridge: process.env.STORYBLOK_IS_PREVIEW === 'true' ? true : false,
})
    

Having the Bridge disabled in production is also a performance optimization measure, as it results in one less script to be loaded.

Providing the correct access token

In order to access the draft version of our content, we need to ensure that the preview access token is used. Fortunately, using environment variables makes it very easy. We can simply use the specified variable directly instead of hardcoding the token. This would typically be done in the same place where we just took care of handling the Bridge:

Using the correct access token
        
      storyblokInit({
  bridge: process.env.STORYBLOK_IS_PREVIEW === 'true' ? true : false,
+  accessToken: process.env.STORYBLOK_TOKEN,
})
    

Enabling a suitable rendering mode

For real-time editing to work in the preview environment, it is usually necessary for it to run in Server-Side Rendering (SSR) or Client-Side Rendering (CSR) mode. In production, you may want to use a different rendering mode altogether, such as Static Site Generation (SSG), for performance reasons. Alternatively, you may also want to run the same rendering mode in both environments but configure additional performance improvement measures in production, such as an optimized caching strategy, HTML compression, etc. Please consult the documentation of your framework of choice as well as our respective SDK documentation to become acquainted with the technical possibilities.

Essentially, you can utilize conditional logic again to configure a suitable rendering mode for each environment:

Conditionally defining the rendering mode
        
      frameworkConfig({
  renderingMode: process.env.STORYBLOK_IS_PREVIEW === 'true' ? 'SSR' : 'SSG',
})
    

Depending on the framework, additional steps may have to be implemented, such as specifying a different build command depending on the rendering mode. This could be configured accordingly in the two environments created on your hosting service.

Automatically rebuilding a statically generated site (SSG)

When choosing to deploy your production environment using SSG, the site needs to be rebuilt to take into account the latest published content changes. Fortunately, this process can be automated by using so-called build hooks and Storyblok's webhooks. Most hosting services let you define a build hook, which, essentially, is a URL that triggers the deployment of the latest production branch and, in our case, fetches the most up-to-date content during the build process.

Once you have configured a build hook for your production environment, in Storyblok, navigate to Settings > Webhooks. Here, you can paste the build hook into the Endpoint URL field. For this particular use case, you will usually want the webhook to trigger the build process whenever any of the Story (published, unpublished, moved, deleted) events occurs.

hint:

Alternatively, you could also trigger a rebuild manually using the Task Manager App, allowing more granular control.

Conclusion

This tutorial covered the best practices for creating and deploying preview and production environments in two separate environments. Regarding two separate environments, configuring two dedicated environments with environment variables for preview and production allows content editors to switch between the two environments on the Visual Editor.

Therefore, you can use those environment variables anywhere you'll need to load the correct content version, enable the Storyblok Bridge where necessary, provide the access token, and enable the correct rendering mode in the framework configuration files.

Authors

Arisa Fukuzaki

Arisa Fukuzaki

Arisa is a front-end engineer with a passion for front-end technologies and programming education. She is from Japan but currently resides in Germany, the Stuttgart area, and works as a Senior Developer Relations Engineer & Docs at Storyblok. She is also a GirlCode ambassador, co-host of the GirlCode Coffee Chat live streaming, and Google Developer Expert (GDE).

Manuel Schröder

Manuel Schröder

A former International Relations graduate, Manuel ultimately pursued a career in web development, working as a frontend engineer. His favorite technologies, other than Storyblok, include Vue, Astro, and Tailwind. These days, Manuel coordinates and oversees Storyblok's technical documentation, combining his technical expertise with his passion for writing and communication.