Astro + Storyblok: SSR preview for instant visual editing
Tags: astro, storyblok, cms, javascript
Published: 2023-02-10 16:00:35

Storyblok is one of the most popular new content management systems. Its visual editing and component-based workflow are intuitive for editors, while also being easy for developers to work with.

While its [`@storyblok/astro`](https://github.com/storyblok/storyblok-astro) integration with the Astro framework seems fairly straight-forward at first, getting the most out of both Storyblok's visual editor and Astro's static site generation is not as simple.

## The Problem

Since Astro is (by default) a static site generator, which generates all pages at build time, editing content in Storyblok will not give your editors the instant feedback they might expect: Even if your Storyblok webhooks are set up correctly, it can take several minutes for the entire project to be rebuilt before the updated content appears. This makes for a very awkward and unintuitive editing experience.

One way to solve this problem is to use Astro in SSR mode. This uses a Nodejs server or serverless functions to render pages whenever they are requested, rather than at build time. This means pages will update as soon as editors change their content. Unfortunately this also means that you'll lose all benefits of a static site, such as the significantly faster page load times and lower operating costs.

## The Solution

In order to get the best of both worlds, we'll want to set up two separate deployments: One that uses Astro's default SSG mode for your users to visit, and one SSR environment which is used as the preview site in Storyblok. This way, editors get instant feedback while editing content, but users will still get a fast, static site.

Assuming you followed the `storyblok-astro` [Getting Started guide](https://github.com/storyblok/storyblok-astro#getting-started), here is a step-by-step guide to enable this dual environment:

### 1. Install Astro SSR adapter of your choice

Astro supports several platforms for server-side rendering. For this guide I will use Vercel's serverless functions. Please refer to the [Astro docs](https://docs.astro.build/en/guides/server-side-rendering/#adding-an-adapter) for other platforms.

```sh
npx astro add vercel
```

### 2. Conditionally enable SSR mode

This is where the magic happens: Based on an environment variable, we switch between Astro's `server` or `static` mode. I chose to call the variable `PUBLIC_ENV` because Astro makes environment variables prefixed with `PUBLIC_` available in client-side code, which might come in useful. We also conditionally load the SSR adapter since it's not needed in static mode.

```ts
// astro.config.{mjs,ts}

import vercel from '@astrojs/vercel/serverless';
// ...

export default defineConfig({
  // ...
  output: process.env.PUBLIC_ENV === 'preview' ? 'server' : 'static',
  adapter: process.env.PUBLIC_ENV === 'preview' ? vercel() : undefined,
  // ...
});
```

### 3. Conditionally enable the Storyblok bridge

The Storyblok bridge is responsible for reloading the page when there are changes to Storyblok content, as well as making individual components clickable in the Storyblok editor. Since we will never open the production static site in Storyblok, we can reduce the bundle size a little by disabling the bridge in production.

```ts
// astro.config.{mjs,ts}

export default defineConfig({
  // ...
  integrations: [
    storyblok({
      bridge: process.env.PUBLIC_ENV !== 'production',
      // ...
    }),
    // ...
  ],
  // ...
});
```

### 4. Create separate build scripts

In order to build the two different deployments, we'll create two build scripts which set the `PUBLIC_ENV` variable to the respective value. For the sake of completeness, we'll also set the variable to `development` when using the dev server.

```jsonc
// package.json

"scripts": {
  "dev": "PUBLIC_ENV=development astro dev",
  "build:production": "PUBLIC_ENV=production astro build",
  "build:preview": "PUBLIC_ENV=preview astro build",
  // ...
},
// ...
```

### 5. Deploy the two sites

Next, we'll create two separate deployments which use the different build scripts. In my case, I'll create two Vercel projects, connect both to the project's GitHub repo and set the *Build Command* under *Settings > General > Build & Development Settings* to `npm run build:production` for the static production site and to `npm run build:preview` for the SSR preview site. You should be able to do the same for Netlify, Cloudflare or similar providers.

### 6. Use webhooks to trigger production deploys

Now all that's left to do is to set up webhooks between Storyblok and your hosting provider so that the production site is rebuilt whenever content is published.

First, create a webhook in your production site's admin interface. For Vercel, I did this under *Settings > Git > Deploy Hooks*.

Then, copy the URL of the webhook you just created, and paste it in Storyblok under *Settings > Webhooks > Story published & unpublished*. Now your production site should be rebuilt whenever your editors publish or delete content!

## Conclusion

This setup made Astro integrate with Storyblok much more nicely, and it is now one of my favorite framework-CMS pairings on the market. The awesome performance and developer experience of Astro static sites paired with the intuitive Storyblok visual editor is wonderful!

I hope this guide was helpful if you're building a site with these two technologies. Let me know if you have any questions or ideas for improvement!

## Extras

There are a bunch of extra tips and tricks about Astro + Storyblok that I wanted to share, but since this guide is already getting long I decided to put them in a separate companion post, which you can find here: [Astro + Storyblok: SSR Tips and Tricks](https://dev.to/jgierer12/astro-storyblok-ssr-tips-and-tricks-3j2k). There I explain how to prevent drafts from showing up in your production site, disable search engine indexing of your preview site and more.