Skip to main content

The Pitchburgh diaries - decoupled Layout Builder wrap-up

Since Drupalcon Pittsburgh, we've been working on a decoupled Layout Builder for Drupal. We've reached the end of the statement of work, so let's recap the requirements and discuss what’s next.

by lee.rowlands /

Requirement 1: Create an npm package for developing decoupled components

Issue: https://www.drupal.org/project/decoupled_lb/issues/3375410

Taking inspiration from @wordpress/scripts, develop an npm package that has tooling for building and packaging decoupled components.

The MVP of this will include:
 • a script for building JS and CSS

Out of scope but for future consideration:
• linting
• a script for checking license compatibility

Release this as a package on npm in the @drupal namespace.

Delivery

Not only does the developed package support building CSS and JS via drupal-scripts build, but we also added support for initialising a new project and code generation with drupal-scripts init and drupal-scripts generate.

Requirement 2: Determine how to deal with externals in Webpack

Issue: https://www.drupal.org/project/decoupled_lb/issues/3375412 

Wordpress has @wordpress/dependency-extraction-webpack-plugin, a webpack plugin that extracts dependencies that are provided by Wordpress.

We don't have enough budget to go down the full 'roll our own' approach and it may be overkill for now. Instead, derive configuration for Webpack externals that ensures components don't bundle dependencies Drupal will guarantee to be present.

This config will likely be part of the package at 1)

Out of scope but for future consideration:
Create our own plugin like Wordpress with some Drupal specific replacements

Delivery

As per our previous post, we used Vite externals and then wrote a module to add support for Import maps to Drupal. We wrote a new version of the React module that works with import maps.

Requirement 3: Provide a registry of React Layout Components

Issue: https://www.drupal.org/project/decoupled_lb/issues/3375413 

Create a simple layout registry and registration process for layout plugins. Create an MVP of some common layout plugins:
One column
Two column
Grid layout

Out of scope but for future consideration:
• Attempt to use the AST-generated Twig APIs to autogenerate React components that provide the markup for a layout plugin.
• Write a symfony/console command that can take the ID of a layout plugin and generate an equivalent React component.
• Bundle this as a part of a new experimental module.

Delivery

We wrote a context provider to create a layout registry and a way to pass this configuration from Drupal to React. We wrote React implementations of one and two column layouts.

Requirement 4: Persistence layer

Issue: https://www.drupal.org/project/decoupled_lb/issues/3375414 

Create an OpenAPI spec for the persistence layer. The MVP will include the following endpoints:
Choose section
Add section
Remove section
Choose block
Add block
Update block
Remove block

These will mirror existing HTML endpoints from layout builder\

Create JSON equivalent versions of these routes. Some of these routes will exist to serve information for the decoupled layout builder (e.g. choose block/section) whilst others will exist to perform mutations of the configured layout on the Drupal side.

Out of scope but for future consideration:
Configure section
Move block

Delivery

We wrote an API specification for the needed endpoints. We didn't need add section, remove section, add block, update block and remove block, as all these are handled client side. We did need additional endpoints for saving, discarding and reverting.

All of these endpoints are implemented, with test coverage in the Decoupled Layout Builder API module

Requirement 5: Create a block discovery mechanism to associate a block plugin with a React component

Issue: https://www.drupal.org/project/decoupled_lb/issues/3375416 

Create a registry component that can translate block plugin IDs to these components. Allow modules to register a component as a React version of a Drupal Block plugin so the decoupled layout builder knows how to render it.
This will take inspiration from registerBlockType in @wordpress/block

Create a React version of the InlineBlock block plugin as a proof of concept
Out of scope but for future consideration:
Convert other Block plugins to React

Delivery

We wrote a context provider to provide a block registry and a way to pass configuration of this from Drupal to React. We wrote a React implementation of the inline block, which uses formatter and widget components. This component is quite complex as it needs to load entity view and form display configuration from Drupal. We have a working version of the Field block plugin too.

Requirement 6: Create a Drupal-rendered block fallback React component

Issue: https://www.drupal.org/project/decoupled_lb/issues/3375416 (could have)

Create a fallback React block plugin component that can make a request to Drupal for a rendered previews. Extend the persistence layer to add another Route for this.

Out of scope but for future consideration:
• Support a legacy configuration form for these blocks as well

Delivery

We ship rendered 'fallback' versions of each block in the API. This saves the individual plugins from needing to make many single requests and ensures the best performance as loading happens once upfront.

Requirement 7: Create a discovery mechanism to associate a widget plugin with a React component

Issue: https://www.drupal.org/project/decoupled_lb/issues/3375417 

Identify a list of initial widget plugins that make sense to convert to React as an initial proof of concept. Create a React version of these widget plugins making use of @wordpress/components.

Create a registry component that can translate widget plugin IDs to these components. Allow modules to register a component as a React version of a Drupal widget plugin so the decoupled layout builder knows how to use it to edit an inline block.

Out of scope but for future consideration:
A React powered media library widget

Delivery

We wrote a context provider to create a widget registry and a way to pass the configuration of this from Drupal to React. We wrote a React implementation of the text area widget, which uses an inline CKEditor for in-place editing with rich formatting. We didn't use WordPress components - more on this below.

Requirement 8: Create a discovery mechanism to associate a formatter plugin with a React component

Issue: https://www.drupal.org/project/decoupled_lb/issues/3375418 

Identify a list of initial formatter plugins that make sense to convert to React as an initial proof of concept. Create a React version of these formatter plugins.

Create a registry component that can translate formatter plugin IDs to these components. Allow modules to register a component as a React version of a Drupal formatter plugin so the decoupled layout builder knows how to render a preview of an inline block.

Out of scope but for future consideration:
• Devise a way for themes to modify the markup of these components, possibly by registering their own replacements in the registry.

Delivery

We wrote a context provider to create a formatter registry and a way to pass configuration of this from Drupal to React. We wrote a React implementation of the text default and string default plugins.

Requirement 9: React context provider/hooks

Issue: https://www.drupal.org/project/decoupled_lb/issues/3375420 

To allow blocks/layouts/widgets/formatters to access this data without passing down props from each component, create a series of context providers and hooks to allow components to access and update this data.

We can take inspiration from e.g. useBlockProps in @wordpress/block-editor

Delivery

We didn't end up using hooks and context providers for this. Instead, we used a Redux toolkit - a mature state management solution designed for large complex applications. There are various selector functions made available so components can select state as well as reducers for updating state. The use of Redux means we can get things like undo and redo without much effort (more on that later).

Requirement 10: Layout builder component

Issue: https://www.drupal.org/project/decoupled_lb/issues/3375421 

Add a component which manages the following UX behaviours:
Adding a section (layout plugin)
Adding an inline block
Editing an inline block

It is hoped that we can take inspiration from existing open-source components in this space including @wordpress/components and builder.io.

Out of scope but for future consideration:
• Editing (Configuring) a section
Moving a block

Delivery

Here we completed the following:

  • Adding a section
  • Adding a block
  • Editing a block
  • Moving a block
  • Moving a section
  • Configuring a section
  • Configuring a block
  • Viewing an outline of the layout, including support for moving components/sections
  • Saving a layout to Drupal, including autosave

You can try this for yourself in our interactive storybook.

Where to next?

The end of the Pitchburgh funding doesn't mean the end of the road. As we progressed through the project, we were building a backlog of out-of-scope items we discovered. A key aspect of this project was the evaluation of the feasibility of a decoupled layout builder. This work will also feed into the newly announced Page Building Experience initiative as one possible solution.

We tracked all the out-of-scope items in a backlog and have now added issues on Drupal.org for both the npm packages and the Drupal module.

We plan to keep working through these as part of our ongoing commitment to open-source. Do you have an interest in helping us? If so, reach out to me in the #layouts channel on Drupal Slack. There's a real mix of technology at use here - not just PHP but React, Typescript and CSS. 

We definitely need some help making the UI look nicer and more closely aligned with the Claro look and feel, as well as the work being done by the Admin UI initiative. If you've been looking for a way to get involved in Drupal contribution without a heavy understanding of Drupal's internals - this might be the ideal place to get started.

Related Articles

What if? Pitching for a Decoupled Layout Builder

It’s time to transform and improve the Layout Builder UI experience. What if we could rewrite it using React? Check out our video pitch (made using React), which received the highest average rank in the Pitch-burgh contest at DrupalCon!