Problem/Motivation
A page is assembled from several displays at once: a page layout, an entity view display, an entity override, a Views display. Each is stored elsewhere and edited in its own builder, so moving between the levels means leaving the tool. The level you need is often one that is not built with Display Builder yet, which is exactly where users get stuck.
Proposed resolution
Add an Instances sidebar panel that lists every display a user may travel to, grouped by buildable provider, with the current one marked.
- It lists displays, not instances, and is strictly read-only: it never creates or saves an
Instanceentity as a side effect of rendering. - Displays not built with Display Builder are listed too, hidden behind a toggle, and linked to their own settings page to enable one there.
- Each row carries a link to where that display is configured (Manage display, the Views edit form, the page layout form). An override points at the display it overrides.
- A filter, and a per-group cap with a View more button, keep the panel usable on sites with many displays.
- A listing that is bounded says so: entity overrides show the most recent 25 per display.
- New documentation page,
docs/how-displays-nest.md, and a workflow sentence in the keyboard help.
Here is a capture of new instances list.

API changes
- New
Drupal\display_builder\DisplayReferencevalue object. DisplayBuildableInterface::collectDisplays()and::collectDisplaysBound()added.DisplayBuildablePluginBasereturns an empty array andNULL, so existing plugins need no change.collectDisplays(),collectDisplaysBound()andcollectInstances()are instance methods, not static. A listing is answered by a plugin built with no configuration, which restores dependency injection: the\Drupal::service()calls inside them are gone, andInstanceListBuilderno longer passes an entity type manager into a static that declared no parameters and ignored it.getPrefix()removed from the interface. It stays on the base class, where every caller is a plugin asking about itself.EntityView::manageDisplayUrl()is public, so an override can reuse it.- Two lazy-resolution fixes of the same shape, both of which made a label silently
NULL:ViewDisplay::getDisplayLabel()read$this->extenderinstead of calling::getExtender(), so Views displays were never listed at all;EntityViewOverride::getDisplayLabel()read$this->fieldinstead of calling::getField(), so it only worked when a previous call had happened to resolve it.
User interface changes
New Instances panel, enabled by default and added to existing profiles by display_builder_post_update_3().
Remaining tasks
- Review.
- No Playwright coverage for the panel yet.
- Follow-up: the four ID-based statics still on the interface, see #3616313: Check static methods on DisplayBuildableInterface.
| Comment | File | Size | Author |
|---|
Issue fork display_builder-3615031
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
Comment #2
mogtofu33 commentedComment #4
mogtofu33 commentedComment #6
mogtofu33 commentedComment #7
mogtofu33 commentedComment #8
mogtofu33 commentedComment #9
mogtofu33 commentedComment #10
ipumpkinThe `entity override instance` should only display the list of instances corresponding to different languages for the current entity when the current entity is being overridden. This list is essentially a translation list. Otherwise, it should not be displayed by default, and the number of instances could be very large.
translation override issue here need review:
https://www.drupal.org/project/display_builder/issues/3611826
Comment #11
mogtofu33 commentedWe will be able to handle translation in it's own Epic to have a full picture and not partial fixes, created as #3615902: Translation support.
Starting point will probably be when #3555110: Symmetric translation is ok, then #3611826: Display Builder override saves to wrong language on multilingual sites.
For now it's not in the scope of this issue but that's good to foresee what could help or block future work. Created specific issue under translation plan.
That's why there is a limit of 25
Comment #12
ipumpkin@Jean Excellent planning. I hope the translation issues can be resolved soon. I'm preparing to use DS in the production environment.
Comment #13
mogtofu33 commentedComment #16
mogtofu33 commentedIssue was too big and not clear, reworked as an instances list enhancement.
An other issue will propose something on the placeholders / non renderable.
Comment #17
mogtofu33 commentedComment #18
mogtofu33 commentedComment #19
mogtofu33 commentedComment #20
mogtofu33 commentedComment #21
pdureau commentedI take time reviewing this MR because, if merged in the current state, it will count for 5% of the PHP code (comment, blank lines & tests excluded) of the full project.
Code overview
Most of the added logic is in
InstancePaneland in the implementations of those 3 newDisplayBuildableInterfacemethods:::getDisplayLabel(): ?string;::collectDisplays(array $options = []): array;::collectDisplaysBound(): ?TranslatableMarkup;Code review
⚠️
InstanceInterface::getBuildable(): ?DisplayBuildableInterface;has been added with the corresponding implementation but it is called only in:Instance::label()so inside the same classInstanceTest::testLabelWithoutBuildable()⚠️ Why
DisplayBuildablePluginBase::getViewModeLabel()and::getBundleLabel()? This logic (and the correspondingentity_type.bundle.info) seems specific todisplay_builder_entity_viewscope, do we?⚠️
::getPrefix()has been removed from theDisplayBuildableInterfacebut it is still a public method inDisplayBuildablePluginBase. Why? For information, in #3573905: Simplify DisplayBuildableInterface we successfully moved this method to protected.⚠️ A new "floating"
DisplayReferenceutility class (called by InstancePanel and implementations ofDisplayBuildableInterface::collectDisplays()). There is nothing in the Drupal API which can "carry" this logic? If not, it is OK.⚠️
ContextualFormPanelis the second most changed file of the MR but it is hard to see how it belongs to the current work. Some added logic is specific toMainPageContentSourcefromdisplay_builder_page_layoutaccording to the comment but todisplay_builder_entity_viewaccording to the code.⚠️ I have a warning in every display builder page, after switching to the branch, a updb and cache rebuild:
Feature review
Still exploring, looks cool so far... but we can discuss the code review first.
By the way
In a #3542796: Preview of view sources comment, it was said:
But i didn't see this while doing the review.
Comment #22
mogtofu33 commentedWill do 1, 2, 3
5 as well, it's caused by history of this task before split. The comment is wrong I'll clean.
4: no core we can use for that I know of, for this mix label+url + state (as seen from constructor), could have a follow up for investigation but I like the return type, it's clean. core Link Or Url miss our specific extra cases. Formatting methods
keywordsandtooltipcould move out as they are display, perhaps InstancesPanel.Comment #23
mogtofu33 commentedInstanceInterface::getBuildable()removed and now a privateInstance::getDisplayLabel()getViewModeLabel() / getBundleLabel()moved to EntityDisplayLabelTrait indisplay_builder_entity_view, andentity_type.bundle.infois gone from DisplayBuildablePluginBase.composeDisplayLabel()stayed on the base: the format is a contract on the interface and three submodules call it. While moving it I also switched the view-mode lookup to core's entity_display.repository, which is one cached array per entity type instead of a config-entity load per row, and it honorshook_entity_view_mode_info_alter().getPrefix(): now protected static, using the approach from your MR 235. The entity_delete hook builds the plugin instead of composing the ID, which on this branch is shorter than ref MR because EntityViewOverride already accepts display and entity config keys. Two side effects worth flagging: a return in that loop should have been continue, so a bundle with two override displays previously stopped deleting after the first miss; and the deletes are now batched into one call. Tests use a literal PREFIX constant, as !235 does, which also means they assert the stored ID format instead of deriving it from the code under test.One correction to my own docblock while I was there: the prefix was never actually encapsulated. InstanceAccessControlHandler, PageVariantSubscriber and BackButton all read
$definition['instance_prefix']directly, each hand-rolling the same scan. Noted in the docblock as a follow-up.DisplayReference kept, and I would argue it is a value object rather than a floating utility. Stronger reason is the contract:
collectDisplays(): DisplayReference[]is implemented independently by four plugins in three submodules, and the alternative is untyped arrays with magic keys that PHPStan cannot check. Moved display methodskeywords()andtooltip()into InstancesPanel.ContextualFormPanel: the
@see MainPageContentSourcewas simply wrong and is gone; the prose now says plainly that the page layout boundary is not handled here. The duplicatedmanageDisplayUrl()is gone too: it asks the buildable throughgetDisplayUrlFromInstanceId()and only adds the field_ui route guard.I will create a follow up for some loose end and non blocking.
Comment #24
pdureau commentedOK, good, no issue with that. I was just asking if there was already something defined in the Drupal world we can leverage.
I will resume my review soon, with a focus on the functional part.
Comment #25
pdureau commentedI still have this warning on display builder pages:
Review in progress.
Instance sidebar
The feature is cool but it looks like a power users one which have a risk to confuse the casual users. We need to be especially careful because of those 2 reasons:
I am genuinely asking myself (no answer yet, not pushing anything for now):
⚠️ The display we are currently building is also shown in the list. When i start a new display, i see it as "empty", it stays empty when i publish it. I need to refresh the page to see the "empty" removed.
⚠️ "Not built yet" means "not manage by display builder" and show displays which can be built from other tools.
⚠️ The settings link of "Entity View Override" is loading entity view display page. Is it the one people doing content override need? Would the entity edit form be better?
ContextualFormPanel
I am still struggling to understand the change: "Build the line naming the display this node renders, when there is one." for 3 reasons:
entity_viewwith a logic around entity types, bundles, view modes, targets...Comment #26
mogtofu33 commentedThanks for the review. Fixed requests, and cut the ContextualFormPanel change.
EntityViewOverride link was for an other issue but temporarily fixed here to avoid a nonsensical link. Point to the edit content, we can see what's best next.
Not all but the 'builder' of the site yes. And the list is based on access, so builder without specific access (Page layout, Views, Field UI...) do not see them. For me it match our priority persona
builder. Even a possible 'Editor'.Yes, my priority is UX for adoption and there is no correlation between code size and focus/clean UX, there is an elegance doing both, but not a priority for me.
I would prefer a clear proposition of wording, the shortness was on purpose for visual, extended to
Not using Display Builderas I want to try to keep one line.A follow up can include better wording, help text...
The fact that it can overlap with other tools is not an issue for me atm, we can plan better if required as follow up.
Comment #27
pdureau commentedGreat, but we have still the corresponding line in
.phpmd.baseline.xml:<violation rule="PHPMD\Rule\Design\WeightedMethodCount" file="/builds/project/display_builder/src/Plugin/display_builder/Island/ContextualFormPanel.php"/>Instances UI
A lot of markup and CSS has been generated for this feature, especially in

/assets/css/instances.css. Because of its specific design implementation, the panel looks weird, a bit out of place in the Display Builder UI, with the bullet list, specific sizing and spacing, underline blue links..:We are using a design system, Shoelace, to keep UI consistency and to delegate design choices and implementation to upstream. So, why not using Shoelace components here? For example:
sl-menu-labelinstead of.db-instances__titlesl-menu-iteminstead of.db-instances__item, the settings link can be put insuffixslotsl-menu-item[disabled]instead of.db-instances__item--not-builtsl-dividerin between groupsAlso:
About Views
There are 2 issues:
Because I have already play with a solution while investigating, I will propose a little commit (in a different MR of course), so i keep the ticket on my side for a few minutes more.
By the way
As previously stated, it was said on a #3542796: Preview of view sources comment:
Is it work expected in another ticket?
Comment #29
pdureau commentedCommit pushed (in a distinct MR to not mess with your work): https://git.drupalcode.org/project/display_builder/-/merge_requests/346/...
Addressed:
I can also try to propose the conversion of
instances.cssto Shoelace if you want.Comment #30
mogtofu33 commentedVery strange I never had this bullet list or empty lines, which theme are you on? On Bootstrap, Test or even Olivero I do not have that?
Edit: Ok I see with
Claroan issue.So let move to button component, I do not think menu is valid here as I want to match neighbor 'Libraries'.
For placeholder as I noted on comment #16 that this job has been removed from this issue as it was making it too big. We'll require an other issue which I have locally but not created yet.
Comment #31
mogtofu33 commentedComment #32
mogtofu33 commentedRebased + views change plus wording.
Moved to web components to avoid bleeding.
Comment #34
pdureau commentedOK thanks, i missed this. Let's wait the next issue.
Cool.
So, we reached RTBC, with only one last change before merging: are you OK to add 2 more conditions for the list of View displays?
They are inspired from
core/modules/views/src/Plugin/views/display/Page.php, lines 260-265, and they skip displays which are rendered with admin theme: /admin/content, /admin/people...However, do we need to consider the View displays rendered with admin theme but where Display Builder has been used?
Edit: Oops, I am not sure we have addressed everything from #3614998: Island instances list navigation enhancement:
Comment #35
mogtofu33 commentedViews filter added plus tests. Follow up issue for wording.