Extending a Core Component's Dialog and Hiding Inherited Fields
How sling:resourceSuperType merges your component's dialog with a Core Component's own dialog, how sling:hideResource suppresses a specific inherited field, and why hiding a Core Component's built-in structure editor without replacing what it does can quietly break the component's own client-side behavior.
Extending an Adobe Core Component is usually sold as the fast path: inherit the HTL, the client library, the accessibility work, the whole editing model, and just add what's different about your version. What doesn't get said as often is that the inheritance runs both ways — your dialog doesn't just add fields, it merges with the Core Component's own dialog, and if you don't want one of those inherited fields, hiding it is a deliberate act with its own failure mode.
Problem
A team builds a custom carousel by extending Adobe's Core Component Carousel (core/wcm/components/carousel/v1/carousel) via sling:resourceSuperType, rather than building one from scratch. This buys the Core Component's HTL rendering, its client-side carousel JS, and its accessibility handling for free — the whole reason to extend rather than reimplement.
But Core Components ship their own dialog too, and dialog inheritance doesn't work like Java inheritance where you only get what you explicitly reference. Through the Sling Resource Merger, your component's _cq_dialog is merged, tab-by-tab and field-by-field, with the Core Component's own _cq_dialog — meaning tabs you never wrote (like an "Accessibility" tab, or an "Items" tab for managing child components) show up in your dialog automatically. If your custom version handles something the Core Component's default tab also handles — say, you built your own way of managing carousel slides — you now have two competing UIs for the same concern unless you explicitly suppress one.
Architecture
Dialog merge inheritance works through two independent mechanisms that combine:
sling:resourceSuperTypeon the component itself (not the dialog) — this is what makes the whole component (rendering, dialog, client library references) inherit from the Core Component. Declared once, at the component's own.content.xml.- The Sling Resource Merger, which resolves
_cq_dialog(and any other resource) by walking the super-type chain and combining nodes with the same name across all levels — your dialog's tabs plus the super-type's tabs, in the order controlled bysling:orderBefore/sling:orderAfter.
To remove a specific inherited node from that merged result, you declare a node with the exact same name in your own dialog and mark it sling:hideResource="{Boolean}true". The merger still walks the super-type's version of that node, but the hide flag tells rendering to skip it. This is the deliberate, correct use of sling:hideResource — as opposed to the "leftover field nobody deleted" case covered in the companion piece on show/hide widgets.
MyCarousel component
sling:resourceSuperType = core/wcm/components/carousel/v1/carousel
MyCarousel's _cq_dialog
tabs/
properties (custom, new)
containerItems <- same name as the Core Component's own child-items tab
sling:hideResource = true
-> suppresses the Core Component's built-in "Items" editor
Merged result actually rendered to the author:
properties (yours)
accessibility (inherited, unmodified)
<containerItems NOT rendered - hidden>
Repository
A real example, a hero carousel extending Adobe's Core Component Carousel, field names generalized:
<!-- .content.xml -->
<jcr:root
jcr:primaryType="cq:Component"
jcr:title="Hero Carousel"
sling:resourceSuperType="core/wcm/components/carousel/v1/carousel"
cq:isContainer="{Boolean}true"/>
<!-- _cq_dialog/.content.xml (excerpt) -->
<containerItems
jcr:primaryType="nt:unstructured"
jcr:title="Items"
sling:hideResource="{Boolean}true"
sling:resourceType="granite/ui/components/coral/foundation/container"
margin="{Boolean}true">
<items jcr:primaryType="nt:unstructured">
<columns jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/coral/foundation/fixedcolumns">
<items jcr:primaryType="nt:unstructured">
<column jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/coral/foundation/container">
<items jcr:primaryType="nt:unstructured">
<containerItems
jcr:primaryType="nt:unstructured"
sling:resourceType="core/wcm/components/commons/editor/dialog/childreneditor/v1/childreneditor"/>
</items>
</column>
</items>
</columns>
</items>
</containerItems>
Note the node name containerItems matches the name used by the Core Component's own dialog for its children-editor tab — that name match is what makes the hide take effect against the inherited node, not just add a second, separately-named hidden tab that does nothing.
How It Works
The Core Component Carousel's HTL and client-side JS both assume slides exist as real child resources under the component — the kind of thing an author adds through that "Items" tab's children editor (drag components in, reorder them, delete them). The carousel's JS looks for a specific DOM structure (data-cmp-hook-carousel="item" elements, one per child) to wire up navigation, indicators, and auto-rotation.
Hiding the "Items" tab only removes the authoring UI for that mechanism. It says nothing about how slides actually get into the component. If the custom carousel's real intent is "author slides through a different mechanism entirely" (a multifield of slide data, for example, rendered by custom HTL instead of the inherited one), hiding the tab is necessary but not sufficient — the component also has to stop relying on the Core Component's own HTL/JS for rendering those slides, or override enough of it that the two don't disagree about where slide content comes from.
Real Project Example
A team building a hero carousel wanted marketing-configured slide counts, per-screen and per-scroll display settings, and custom icon-picker fields — genuinely new authoring needs the stock Carousel dialog didn't have. They added their own "Properties" tab for those, then hid the inherited "Items" tab, intending to manage slides through a separate multifield-driven approach for a related simplified card variant.
For the full hero carousel itself, though, the actual slide content still needed to come in as real child components (image/text/CTA blocks), same as the stock Core Component expects — hiding "Items" there would have removed the only authoring path for adding them, since nothing replaced it. The team caught this in review before shipping: the fix was to not hide the tab on the full carousel (keep the inherited children editor, since it's genuinely still needed), and only hide it on the separate lighter-weight card variant that really did manage its content a different way. The lesson that came out of it: hiding an inherited tab has to be justified per-component by asking "does something else provide the same capability this tab provides," not applied by copy-pasting a hidden-tab snippet from a similar-looking component.
Production Troubleshooting
- Before hiding an inherited tab, confirm what capability it actually provides, not just what it's labeled. The Core Component
childreneditorresource type manages actual child resources — hiding it without another way to add children means the component becomes un-authorable for its main content, silently (no error, the tab just isn't there anymore). - Node-name matching for the hide is exact and case-sensitive. If the Core Component's own tab is renamed in a future Core Components version, your hide node's name has to be updated to match, or it stops suppressing anything and the inherited tab reappears.
sling:orderBefore/sling:orderAftercontrol merge ordering, not suppression. They're a separate concern fromsling:hideResource— don't reach for ordering attributes when what's actually needed is hiding, and vice versa.- Check the Core Component's client-side JS assumptions, not just its dialog, before assuming a hidden tab is a clean removal. The dialog and the runtime behavior are two different inheritance surfaces that happen to be connected by the same
sling:resourceSuperTypedeclaration.
Why Architects Care
Extending a Core Component is a genuine productivity win — free accessibility, free client-side behavior, free upgrade path as Adobe patches the underlying component. The cost is that inheritance is bidirectional and implicit: you inherit the dialog structure whether you meant to or not, and suppressing a piece of it is a decision with runtime consequences, not just a cosmetic authoring-UI choice. Reviewing a sling:hideResource node in a dialog that extends a Core Component should always prompt the question "what does the thing I'm hiding actually do, and what's replacing it," the same scrutiny a code reviewer would give to deleting a method override.
Summary
sling:resourceSuperTypeon a component makes its dialog merge with the super-type's dialog, node-by-node, via the Sling Resource Merger — not just at the component's own explicit request.- A node with the same name as an inherited node, marked
sling:hideResource="{Boolean}true", suppresses that specific inherited node from the merged result — this is the deliberate mechanism for removing an unwanted inherited field or tab. - Hiding the Core Component Carousel's inherited "Items"/children-editor tab removes the authoring UI for adding real child slides — it does not, by itself, change what the inherited HTL/JS expects to render.
- Always verify, per component, whether something else genuinely replaces an inherited capability before hiding it — a hidden-tab snippet that's correct on one component is not automatically correct on a visually similar one.
What's Next
The companion real-world post below traces what actually happens at runtime when a carousel's inherited children editor is hidden without a working replacement — navigation controls render, but there's nothing for them to navigate.
Want to See This Applied to a Real Problem?
See The Carousel With No Slides to Navigate for the full incident: a hidden inherited tab, a client-side carousel that initializes against zero child items, and the test that would have caught it before launch.
Enjoyed this chapter?
Get an email when I publish the next chapter. No spam — just new technical deep-dives.
Comments
Share feedback or questions about this blog post.
No comments yet. Be the first to share your thoughts.