Build Page Manager page variants visually with the Display Builder.
The variant's whole source tree is persisted in the page variant's own
variant_settings, so it travels with the page configuration
(export, duplicate, translation overrides). The dynamic contexts the page
collects at runtime are forwarded to every source, so context-aware sources
(entity fields, tokens, ...) behave consistently on the builder and
front-end paths.
Features
1. The "Display Builder" page variant plugin
DisplayBuilderVariant
(@PageDisplayVariant(id = "display_builder_page_manager"))
implements ContextAwareVariantInterface:
-
The variant settings store
profile(the Display Builder
profile) andsources(the source tree). -
build()merges the builder-side contexts
(getRuntimeContexts()) with the
real request contexts of the page variant (injected by
page manager throughsetContexts(), gathered by the
PageManagerEvents::PAGE_CONTEXTsubscribers: current user,
language, upcast route parameters), real values winning on name
collisions; every context's cacheability is bubbled to the render result. -
getWizardOperations()adds the "Display Builder" step to page
manager's edit wizard (DisplayBuilderForm); both the wizard's
General step and the variant configuration form carry a
"Build this variant" shortcut link.
2. The buildable plugin (builder integration)
PageManagerVariant
(@DisplayBuildable(id = "page_manager_variant")) registers a
page variant as a buildable "display":
-
Configuration is storage: the source tree and profile are read from and
written topage_variant.variant_settings
(ConfigBuildablePluginBase); saving clears the plugin cache. -
Instance IDs look like
page_manager_variant__<variant_id>; hyphens in the
variant machine name are escaped toDASHso the ID stays a
valid CSS, JS and HTMX identifier. -
collectInstances()/collectDisplays()aggregate
all variants of this type into the builder's instance and display lists;
getPreviewPagePath()returns the preview path for fixed-path
pages (NULL for wildcard paths); previews render inside the theme's page
chrome (previewWithChrome()). -
Access control: the
administer page_managerpermission (both
the legacy staticcheckAccess()and the beta9
AccessibleInterface::access()).
3. Dynamic context awareness (the core of this module)
The builder runs on an admin route, so the page's route parameters are not on
the request. getRuntimeContexts() fills the gap in four layers so
that context-aware sources are visible, selectable and configurable in the
builder, while front-end rendering always resolves real values:
-
Page context collection:
PageVariant::getContexts()dispatches
PageManagerEvents::PAGE_CONTEXT, so the static contexts, the
current user, the language contexts and the route parameter context
definitions are all in place (route parameter values empty). -
Sample entities (
fillSampleContexts()): every
entity:*typed context without a value is stood in by an
unsaved sample entity from
ui_patterns.sample_entity_generator, marked by
DisplayBuilderHelpers::markSampleEntity()so no formatter
mistakes it for real content. Sample contexts carrymax-age: 0. -
The names ui_patterns expects
(aliasEntityContext()): ui_patterns' context-aware sources ask
for contexts by literal name -[Entity]➜
[Field]and friends want one namedentity, the
derived per-field definitions additionally wantbundle. Page
manager names its contexts after route parameters (node,
current_user), so the entity context of the page's route
parameter (preferred) or any other non-user entity context is also
published asentity, together with abundle
(string) context. -
The access configuration is authoritative
(accessConditionBundles()/applyConfiguredBundle()):
theentity_bundleconditions on the page's Access tab declare
the bundles the page actually serves. The sample entity is generated for
that bundle, and thebundlecontext answers with it under an
AllowedValues constraint - the field lists are thereby narrowed to exactly
the fields of the served bundle.
Front-end rendering (DisplayBuilderVariant::build()) merges the
real contexts over the builder ones by name, then
stripSampleContexts() removes any sample that leaked through
(value is an unsaved entity, or max-age: 0), and the
entity / bundle aliases are rebuilt from the real
values - a visitor never sees sample content, and when a real request lacks
the route parameter the bound sources fall back to display_builder's
degradation path instead of erroring.
4. Builder UI route
/admin/structure/page-manager/display-builder/{page_variant}
(DisplayBuilderController), requiring the
administer page_manager permission plus view access
on the variant.
hook_display_builder_page_displays() registers "this page's
variant" as the display entry point in the builder.
Dependencies
Module Used fordisplay_builder (>= 1.0.0-beta9)
Builder core: buildable plugin system, Instance, Profile, island UI, degradation validation
page_manager
Page and variant entities, the PAGE_CONTEXT event, the wizard (page_manager_ui optional, the wizard form uses it)
ui_patterns
Source filtering (SourcePluginManager), SampleEntityGenerator, RequirementsContext, ComponentElementBuilder
Drupal ^11.4
PHP 8.3+, hook attributes, the PageDisplayVariant attribute
Install with composer require drupal/display_builder_page_manager and enable it. To use it: pick the "Display Builder" variant plugin on a page variant and select a profile in the wizard step.
Known behaviour
-
Sample entities are never saved: preview values in the
builder come from unsaved sample entities (uuid-marked); once published,
the front end reads the real route parameter. When a real request lacks the
parameter (e.g. an optional one), sources bound to that context render a
placeholder through display_builder's degradation mechanism and log a
warning. -
A static context resolving to nothing is treated the same
way; bindings resolved in the Context/Access configuration
(context_mapping) are unaffected. -
Multiple bundles in one access condition: the sample and
the published bundle context answer with the first configured bundle (the
AllowedValues constraint still lists all of them), so the field lists show
the first bundle's fields. A single bundle context can only answer for one
value. -
Preview paths are unavailable for pages whose path is a wildcard
(getPreviewPagePath()returns NULL).
Ask your assistant about Display Builder for Page Manager
Check compatibility with your Drupal and PHP version, maintenance and security coverage, from current release data. How it works
Depends on
Dependencies of the latest stable release
No dependencies recorded for this project.
Required by
Tracked projects that depend on this one
No tracked projects depend on this one yet.
Activity
Releases
| Version | Type | Core | PHP | Release date | |
|---|---|---|---|---|---|
| 1.0.0-beta1 | Pre-release | 11 | Oct 28, 2026 |