Drupal 10 Single Directory Components + Storybook

Albert Skibinski
4 min readDec 18, 2023

--

Image generated by ChatGPT

Note: This is an update on the blogpost I wrote in 2019 about integrating Drupal with Storybook.

Update 02/2024: There is also a new Drupal Storybook module which uses a different approach. It does not use Twig.js but uses a drush command to convert stories written in Twig to JSON and then uses a Storybook setup as server to server-side render the JSON into components (instead of client side rendering the components directly as done in this post).

Single Directory Components

Since Drupal 10.1 we can use Single Directory Components (SDC) as an experimental module. More background info on that here, but in this post we use SDC and integrate it with Storybook using Vitejs as a more modern builder (instead of webpack).

So can we leverage that and hopefully improve our setup from 2019 and simplify usage? Yes! Because thanks to SDC we can now use a more complete pattern of embedding/including components in twig templates like this:

{{ include('my_theme:my_component', {
title: 'Hello world!',
some_other_prop: false
}, with_context = false)
}}

The component is automatically retrieved based on the ID (theme or module name and component name, separated by “:”) so we do not need to use the namespaces anymore provided by components module and defining libraries ourselves. Any css and js with the same name are automatically attached too so we do not need to do that manually anymore!

Setup Storybook with ViteJS

If you’re not familiar with Storybook I wrote a short intro on that in the older post.

We start again by using Storybook for HTML but storybook also needs a builder. We use Vitejs since we want everything blazingly fast.

npm create vite@latest vitejs -- --template vanilla

This will scaffold a new vitejs build app in a folder called “vitejs”. You can name it whatever you want. Package.json files will be there, and some other files. Read more in the create vitejs guide.

Then, install storybook for HTML.

npx storybook@latest init --type html

Now, start storybook and it should work, but without any integration yet. You should see the demo components provided though.

npm run storybook

Twig.js

Now we will need some additional libraries to handle Twig templates. Why? Well, Twig templates are server-side templates which are normally rendered with TwigPHP to HTML by Drupal, but Storybook is a JS tool. In order to render our Twig templates in Storybook, we need to make Storybook understand Twig.

We need the JS equivalent of TwigPHP and that is exactly what Twig.js is.

Note: a different approach is using the cl_server module which allows to render Twig in storybook by Drupal/php. This has benefits but it also requires a working Drupal server.

Twig.js will be automatically added as a dependency when we install the Vite Plugin Twig Loader to load twig files with our vitejs builder.

npm install --save-dev vite-plugin-twig-drupal

Besides twig.js the plugin will also add drupal-twig-extensions and drupal-attribute as dependencies.

Note that if your Twig templates contain custom Twig functions which you will need to convert these to JS if you want these templates to function in Storybook.

ViteJS configuration

Add a vite.config.js file with these contents, note the path to your components.

import { defineConfig } from "vite"
import twig from 'vite-plugin-twig-drupal';
import { join } from "node:path"
export default defineConfig({
plugins: [
twig({
namespaces: {
components: join(__dirname, "../components"),
},
}),
],
})

Storybook configuration

You should have a preview.js and main.js file in your .storybook folder.

In main.js change the path to your components to “components” instead of “stories” since that folder will be the folder in your theme containing the single directory components.

Note that you can arrange the folder structure anyway you like and the path to your components can change based on your vitejs app root.

stories: [
"../../components/**/*.mdx",
"../../components/**/*.stories.@(js|jsx|mjs|ts|tsx)",
],

Create a component

Follow the quickstart to create a single directory component in your Drupal theme.

After that you should have a component in your theme components folder, something like: my_theme/components/my_component.
In there you should have at least a twig file and and a my_component.component.yml file for SDC.

You should already be able to use this component from any other twig template by embedding it:

{{ include('my_theme:my_component', {
title: 'Hello world!',
some_prop: false
}, with_context = false)
}}

In addition, create a *.stories.js file so storybook also recognizes the thing.

my_component.stories.js:

export default {
title: 'Components/MyComponent',
};

// Twig file.
import myTemplate from "./my_component.twig";

// CSS file.
import './my_component.css';

// JS file.
import './my_component.js';

export const myComponent = () => (
myTemplate({
title: 'Lorem ipsum!',
})
);

Use it!

Now, you should be able to use the component anywhere in Drupal (using include, embed or in an render array) and have it available in Storybook too!

--

--