Goal

Render an entity page (e.g. /node/x) using experience builder components converted to custom elements.

Command icon Show commands

Start within a Git clone of the project using the version control instructions.

Or, if you do not have SSH keys set up on git.drupalcode.org:

Comments

fago created an issue. See original summary.

fago’s picture

Issue summary: View changes

As a preparation for figuring out how to integrate best, here is an AI-based summary of how XB takes over entity generation.

Summary: How Experience Builder Takes Over Entity Rendering

Experience Builder (XB) uses a sophisticated multi-layered approach to take over entity rendering on paths like
/node/1:

1. Page Display Variant System

- PageVariantSelectorSubscriber listens to RenderEvents::SELECT_PAGE_DISPLAY_VARIANT
- When page regions are configured for the active theme, it sets the display variant to XbPageVariant
- This runs with priority -100 (after other subscribers like Block module)

2. XbPageVariant Plugin

- Takes over the entire page rendering when selected
- Uses the theme's page.html.twig template
- Populates each region with XB component trees from PageRegion config entities
- The content region is special - it always contains the main content
- Uses PHP Fibers to inject page-level data (title, messages) into components

3. Entity-Level Integration

- ContentTemplateAwareViewBuilder decorates all fieldable entity view builders
- Checks if entity has an XB field (component_tree field type)
- Currently hardcoded to only work with:
- xb_page entities
- node entities of bundle article

4. Detection Logic

The system determines if XB is active through multiple checks:

// In ComponentTreeLoader::getXbFieldName()
1. Entity type must be 'xb_page' OR 'node' with bundle 'article'
2. Entity must have a field of type 'component_tree'

// In ContentTemplateAwareViewBuilder::buildComponents()
3. Looks for ContentTemplate for the entity/view mode
4. Falls back to standard rendering if no XB field found

5. Field Type System

- ComponentTreeItem field type stores XB component trees
- NaiveComponentTreeFormatter renders the field (currently limited)
- Field contains hierarchical component data with UUIDs, props, and slots

6. Rendering Flow

1. Request to /node/1
2. PageVariantSelectorSubscriber → selects XbPageVariant
3. XbPageVariant::build() → renders page regions
4. For main content region:
- ContentTemplateAwareViewBuilder checks for XB field
- If found, uses ContentTemplate or XB field rendering
- Otherwise, falls back to standard entity rendering
5. Component trees are rendered with proper context injection

Key Limitations (as of current code):

- Only works with xb_page entities and article nodes
- Requires manual field addition to content types
- No UI for enabling/disabling XB per content type
- Hardcoded restrictions prevent use with other entity types

Cache Integration

- Adds cache keys like 'with-xb' or 'without-xb' to entity render arrays
- Ensures proper cache invalidation when XB status changes

This architecture allows XB to completely take over page rendering while maintaining compatibility with existing Drupal
rendering systems when XB is not active.

fago’s picture

Issue summary: View changes
fago’s picture

I do think the best and right now also the easiest way to do this is by adding a new, dedicated ce-field formatter.

The frontend can take care of showing the XB-content as a full-page and deal with other fields as needed.
This goes inline with the layout-builder integration which we recently improved to have fields next to the layout, so just keeping the output as yet another field makes sense.

I'd not use the "auto" processing to support, with the formatter we are more flexible when we need settings in the future + the existing "auto" output might be an interesting alternative for some folks, it's a verbatim output of the internally stored structure, let's better keep that there.

What probably makes sense is that we take care of auto-configuring the formatter properly in the future, so the auto-configuration based upon the field-type is something we might want to look into, but that's a generic improvement, and not in scope of this task.

useernamee’s picture

Status: Active » Reviewed & tested by the community

Code looks good and provides a relevant Kernel test. RTBC

fago’s picture

Status: Reviewed & tested by the community » Needs work

I figured the resulting structure is weirdly nested though, this needs improvement. I'll take a stab on it before merging this.

I figured we need a way to make the normalize flatten a custom-element with slot-content in a way that the custom-element turns into a list of custom element elements. Without that it would become tedious to flatten things, since do not want to have a helper like convertRenderArray() to return null|¢ustomElement|CustomElement[]. We should be able to organize a list of custom elments in a single object, that helps to keep code clean.

Thus I'm taking a stab on adding this here. I'd call it "renderless-container", similar to the concept of "renderless-components" in the frontend world. This is not very nice, but expressfull, so when you run into it it should be clear what it is. Since the element is not rendered, we cannot really render the attributes either, so the result is that it only renders the children/slots.

fago’s picture

Status: Needs work » Needs review

ok, new MR is ready with a couple of improvements/fixes:

- adds new renderless-container for better wrapping of multiple children, as described above
- adds template-suggestion for overriding markup output by tag
- implements renderless-container in both markup and json serialization + adds test-coverage for that
- improves XB field-formatter to avoid nesting, XB-fields are single-valued
- improves XB-field-formatter test to assert the flat structure when renering multiple components

useernamee’s picture

Status: Needs review » Reviewed & tested by the community

I reviewed the MR 127. Let's get it merged before #3538463: Leverage renderless-container for simpler code.

The only remark that needs another look is from the .module file (template suggestions but the only template there currently exists is for renderless-container).

  • fago committed d3e5e78e on 3.x
    Issue #3537942 by fago: Add ce-field-formatter for experience builder...
fago’s picture

Status: Reviewed & tested by the community » Fixed

Thx merged I'm going to rebase the other MR then.

> The only remark that needs another look is from the .module file (template suggestions but the only template there currently exists is for renderless-container).

Yes, it generally allows you to add a template for special custom-element, e.g. you could add "custom-element-drupal-markup" if somehow you want to customize it. I think that makes sense and comes with no down-side compared to only adding a single suggestion.

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.