Add a headless CMS with Live Preview to Qwik in 5 Minutes
Storyblok is the first headless CMS that works for developers & marketers alike.
This short tutorial will show you how to integrate the data from the Storyblok API into a Qwik app to create a site. 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 Qwik website 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.
If you’re in a hurry, check out the Qwik Storyblok boilerplate repository on Github!
Environment Setup
Requirements
To follow this tutorial, make sure to meet these requirements:
- Basic understanding of Qwik and Javascript
- Node.js LTS version
- An account in the Storyblok App
The project in this tutorial and its subsequent parts was developed using the following versions:
- @builder.io/qwik@1.5.1
- @storyblok/js@3.0.7
Please remember that these versions may be slightly behind the latest ones.
Create a Qwik project
What’s Qwik offering us
Qwik is designed from the ground up to address the size problem. Small bundle size is its initial goal, and all other design decisions are subservient to that goal.
Qwik is not about creating less JavaScript. Qwik is about not having to ship all that JavaScript to the client at once on application startup. Qwik is what you end up with when you take the idea of "delay loading of JavaScript" to the extreme.
Yes, Qwik requires a different way of thinking and designing your application, but the result is near zero initial JavaScript with progressive JavaScript download based on user interactions.
Let’s create our Qwik app
Let's follow the official Qwik documentation to create an app with the latest version of Qwik. Run this command:
Here, you will be presented with a few options - for the sake of this tutorial:
- Where would you like to create your new project?
./qwik-ultimate-tutorial-series
- Select a starter
Empty App
- Would you like to install pnpm dependencies?
Yes
- Initialize a new git repository? (Up to you)
- Finishing the install. Wanna hear a joke?
Yes
(Who doesn’t want to laugh sometimes)
Once you have created the project, go to the directory by running cd qwik-ultimate-tutorial-series
and launch the project by running pnpm start
. Now you’ll see this screen when you open http://localhost:5173/ in your browser:
Create a new space in the Storyblok app by choosing the Create space {1} option. Pick a name for it {2}. Optionally, you can choose between different server locations for your space {3} (if you choose the United States or China, please be mindful of the required API parameter explained hereinafter).
Shortly afterward, a Storyblok space with sample content has been created for you. Let’s open the Home story by first clicking on Content {1} and then on Home {2}:
Now you’ll see the default screen and the Visual Editor:
Enabling the Visual Editor
In order to actually see your Qwik project in the Visual Editor, we’ll have to define the default environment URL. Let’s do that by going to Settings > Visual Editor {1} and setting the Location field to https://localhost:5173/ {2}:
As Storyblok v2 requires that your website is served via HTTPS. You will need to follow the steps below to enable this in your Qwik project:
- Install
vite-plugin-mkcert
on your project. The installation instructions can be found in the package README vite-plugin-mkcert.
pnpm add vite-plugin-mkcert -D
- In the project’s
vite.config.ts
include:
// other imports
import mkcert from'vite-plugin-mkcert';
export default defineConfig((): UserConfig => {
return {
plugins: [/* other plugins */, mkcert()],
// ... (preview)
};
});
- Finally, run the
pnpm start
command again to see the localhost in https.
Now, if you go back to the Home story, you won’t see your Qwik app there just yet. Just one more quick step to take: Open the Entry configuration {1} and set the Real path to /
{2}. After having saved, you should now be seeing your Qwik app in the Visual Editor:
Connecting Qwik to Storyblok
First of all, let’s install the official Storyblok JS SDK:
This SDK allows you to interact with the Storyblok API. On top of that, it also provides a bridge to enable real-time editing! Let’s start configuring it.
Before we jump into the code, we quickly need to grab our API token from our space. Let’s do that by going to Settings > Access Tokens {1} and copying the Preview Token {2}.
We can use this token to initialize the JS SDK in our Qwik app, so for that, let’s add a .env
file in the root of your project and create the variable PUBLIC_STORYBLOK_TOKEN=<YOUR_COPIED_PREVIEW_TOKEN>
.
Check how to access PUBLIC environment variables in Qwik.
Now, let’s create a plugin in the src/routes
folder of our application:
This will help us reuse the same instance of the JS SDK across our application.
Setting the correct region
Depending on whether your space was created in the EU, the US, Australia, Canada, or China, you may need to set the region
parameter of the API accordingly:
eu
(default): For spaces created in the EUus
: For spaces created in the USap
: For spaces created in Australiaca
: For spaces created in Canadacn
: For spaces created in China
Here's an example for a space created in the US:
Note: For spaces created in any region other than the EU, the region parameter must be specified.
Rendering Dynamic Components in the Qwik App
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 blocks)
- 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 Qwik app)
When you create a new space from scratch, Storyblok automatically creates four default components for you:
page
: Content type blockgrid
: Nested blockfeature
: Nested blockteaser
: Nested block
You can find all of these in the Components section of your space.
Understand the difference between the nestable components and content type in our Structures of Content tutorial.
You may be wondering why we added those components to a storyblok
subfolder. By doing that, we can separate components that load dynamically from the Headless CMS from the ones that are basic to our application, like the router-head.tsx
that comes from the initial setup.
Dynamic components with TSX
To register and render the components coming from the Storyblok API JSON response, we need to define a dynamic component that will be able to render the components we have defined in the storyblok
folder by just providing the technical name:
Check how to integrate Qwik with Tailwind in their official docs.
All we have to do is replace src/routes/index.tsx
with the following code:
As you see, we are using Route Loaders, to load the data from Storyblok API into the server and render it in the component; they can only be declared inside the src/routes
folder, in a layout.tsx
or index.tsx
file, and they MUST be exported.
Route Loaders are perfect for fetching data from a database or an API. For example, you can use them to fetch data from a CMS, a weather API, or a list of users from your database.
At this point, the components should be rendered successfully when viewing the Home story in the Visual Editor.
Real-time editing with Storyblok Bridge
The power of Storyblok relies on its fantastic real-time editing experience. Play with changing the teaser headline or re-arranging the features and see the magic happen!
Fortunately, @storyblok/js
makes a bridge available for you. Your components have to be connected with Storyblok and listen to changes by its Visual Editor. Let's take a closer look at how this is achieved:
First, to link your Qwik and Storyblok components together, @storyblok/js
provides a storyblokEditable
function. If you take a look at the components in your src/components/storyblok
folder, you'll already find it there.
Second, we need to load the Storyblok Bridge in our routes. Let’s create a reusable hook in the plugin@storyblok.ts
with the following code:
Check how you can Register DOM events in Qwik with useOn(), useOnWindow(), or useOnDocument().
Now Let’s use this hook in index.tsx
file and see it in action.
Wrapping Up
Congratulations! You now have a Qwik app with dynamic components, a landing page, and a complete integration with Storyblok, providing a unique real-time editing experience.
As you plan to deploy and use Storyblok and Qwik in production, here are a few important considerations:
- Preview Mode: When running
npm run preview
, remember that Qwik sites have caching enabled by default. You can checkvite.config.ts
androutes/layout.tsx
to see the cache logic. To ensure a real-time editing experience during preview, make sure to remove the cache settings. - Separate Environments: We recommend setting up two environments: one for Storyblok preview without caching (showing draft content) and one for production with appropriate caching (showing published content). You can manage both environments from the same codebase by controlling the settings using
.env
variables.
By following these best practices, you’ll ensure a smooth and efficient workflow, maximizing the benefits of both Storyblok and Qwik.
Resource | Link |
---|---|
Storyblok Qwik Starter repo | https://github.com/storyblok/qwik-5-min-tutorial |
Storyblok JS Module | https://github.com/storyblok/storyblok-js |
Storyblok APIs | https://www.storyblok.com/docs/api |
Qwik | https://qwik.dev/ |