Component libraries have become one of the first things organizations require for new engagements. There are many reasons for organizations to want a component library, but the reality is that Drupal, and PHP in general, have not been in a good place to deliver great solutions in this area. This is why we have created the Component Libraries suite of modules. This article will walk through what we mean when we say component and why we think these modules offer an excellent, flexible solution.
Drupal Components
Before we talk about component libraries, let's clarify what we mean by Components.
In our opinion, a Drupal component is a combination of:
- A Twig template.
- Metadata describing the input data the template accepts.
- Optional JavaScript.
- Optional Styles.
In this context, a component is not a type of block plugin (like Component and Decoupled Blocks modules). Those are progressive decoupling approaches. It's a different problem space, and in that space, we leverage the JS Widgets modules.
The Components module (the plural is an entirely different module from the singular) is a very popular and useful module, but it does not help us declare or render components. Its function is to simplify Twig namespaces and provide some additional Twig functions and filters for use in Drupal templates. Also, a different problem space. In fact, you can leverage this module with our solution.
We developed the Component Libraries: Components (aka CL Components) as a way to way to declare and manage components in Drupal. Our understanding of components aligns with the Single File Components module and the UI Patterns module. We only differ in how components are declared and used. CL Components takes the simplest and most familiar approach, where the developer writes the Twig, JS, and SCSS in separate files and then embeds the components in the Drupal templates (like node--article--card.twig.html
). Check the documentation if you want to learn more about how to declare components. Since CL Components is still in early development, we are researching ways to make these three compatible.
Moving away from Twig.js
All this time, we have been emulating Drupal in our component libraries. This was a solution to a very real problem: most component libraries are written in JavaScript. Storybook is a React application, Fractal is written in JavaScript, and so is Pattern Lab (and it is no longer maintained as it used to be.) In all of these cases, a JavaScript application needs to render Drupal components, which are PHP. There is no simple way for these JavaScript applications to interpret PHP code.
The solution we've been using in the past is Twig.js. This is the compatibility layer between Twig, a PHP project, and JavaScript. From their GitHub page:
Twig.js is a pure JavaScript implementation of the Twig PHP templating language (http://twig.sensiolabs.org/)
The goal is to provide a library that is compatible with both browsers and server-side JavaScript environments such as node.js.
Twig.js is currently a work in progress and supports a limited subset of the Twig templating language (with more coming).
The problem with sending our Twig templates to Twig.js so they can be rendered in our component library is that they don't match Drupal. When Twig.js renders a template, it turns a my-template-file.twig
file with some example data into output.html
. On the surface, this seems enough. However, this workflow does not take into account:
- The styles of your Drupal theme.
- The JavaScript of your Drupal theme.
- Any additional styles and JS added by custom & contrib modules.
- Preprocess functions that alter the HTML or massage the data.
In summary, this workflow does not take Drupal into account. How can we confidently tell our clients that what they see in Storybook is what they will see in Drupal? The uncomfortable truth is that we cannot. How can this work if this workflow doesn't know what Drupal theme should be used? We can try to make the emulation of Drupal as realistic as possible, but as soon as you change a line of CSS, install a new contrib, or add a preprocess, the emulation is outdated.
On top of that, the emulation of PHP will inevitably have rough edges. A consistent transformation of my-template-file.twig
into output.hml
is not guaranteed. Twig.js only implements part of the Twig specification and only for the most recent version of Twig, which is not Drupal's. Aside from that, Drupal may leverage custom and contrib Twig extensions. If you want to render a template with one of those using Twig.js, you will have to write the pure JavaScript implementation first.
As you can see, there are many problems and wasted effort in emulating Drupal for your component library. Our solution is to stop emulating Drupal and start using Drupal to render your components using the CL Server module.
Make Drupal render the components
Storybook started working on server-rendered components back in 2020. Even if they are not yet, present on the documentation site, the Storybook team has pledged compatibility for the server framework. This means that we can use Storybook as our component library and instruct it to make HTTP requests to Drupal to render the components. If you are interested, you can look at the step-by-step tutorial to configure Storybook + Drupal.
The CL Server module will allow you to render a component in isolation. To do so, the module uses your theme to render a full page with your component in a way that is usable for Storybook. Then the @lullabot/storybook-drupal-addon
removes all the unnecessary bits (like menus, sidebar blocks, etcetera). The result is an HTML document that contains only the component as rendered by Drupal. Storybook will use that HTML to display the component. The Storybook Drupal addon will let you select what Drupal theme you want to use because the same component renders differently depending on the theme.
Using this setup, you can ensure that my-template-file.twig
will render the same as in your Drupal pages. As soon as you change a line of CSS, install a new contrib, or add a preprocess, the component library is properly updated. No effort required.
The result is a component library that showcases components that stay true to your Drupal themes and enables modern development techniques for Drupal developers. Techniques that, in the past, were reserved for JS devs.