What is Magento Layout XML?
Short answer
Magento Layout XML is the set of XML files Magento 2 uses to decide which blocks go into which containers on a page. Each request matches one or more layout handles, such as catalog_product_view or cms_page_view_id_<identifier>. Magento merges the XML for those handles from every module and the active theme into one tree of <container> and <block> nodes, then renders it. Theme files are merged after module files, so the theme gets the last word. Most of the storefront changes I make for clients start with a small layout file, not a template copy.
- Controller picks the layout handle for the request 01
- Magento gathers all matching XML from modules + theme 02
- XML merger resolves overrides, removes, and moves 03
- Final tree is traversed; each block renders its template 04
- Merged layout and HTML end up in cache 05
Most common mistake Using dashes in handle names instead of underscores
Five steps from URL request to rendered HTML
Nothing about layout XML is random. Magento merges the matching files from every module and the theme into one tree, in a fixed order. This is what happens between the request and the HTML.
-
01
Controller picks the layout handle for the request
Every storefront request ends in a controller action, and the page it returns gets a list of layout handles.
defaultis always there. Then comes the full action name, likecatalog_product_vieworcheckout_cart_index, and often a handle for one specific entity, likecms_page_view_id_<identifier>for a CMS page. Each handle name maps to a file name, socatalog_product_viewmeans Magento reads everycatalog_product_view.xmlit can find. -
02
Magento gathers all matching XML from modules + theme
With the handles known, Magento goes through every enabled module and the active theme (plus its parent themes) and collects each
view/frontend/layout/<handle>.xmlthat matches. A product page pulls files from many modules: Magento_Catalog, Magento_Review, Magento_Wishlist, your own extensions and the theme. At this point it is just a pile of XML fragments waiting to be merged. -
03
XML merger resolves overrides, removes, and moves
The fragments are merged in module load order (the
<sequence>in eachmodule.xml), with theme files last.remove="true"takes a block out,<move>puts it somewhere else, and<referenceBlock>or<referenceContainer>lets you add children to something another module declared. When two files set the same argument or template, the later one wins. You end up with one layout tree for this request. -
04
Final tree is traversed; each block renders its template
Magento then renders the tree from the top. For each
<block>it creates the PHP class named inclass="...", passes the<arguments>in as block data and callstoHtml(), which renders thetemplate="Module_Name::path/file.phtml". A<container>has no class or template. It only wraps its children, optionally in an HTML tag such as a<div>, and joins their output in order. -
05
Merged layout and HTML end up in cache
The merged layout is cached in the
layoutcache. Blocks that set a cache lifetime keep their HTML inblock_html, and the finished page goes intofull_page. On a warm store, that is why a layout change seems to do nothing until you runbin/magento cache:clean layout block_html full_page. It is the first thing I check when a client says their XML change is not showing.
Four scenarios where layout XML is the right tool
I use layout XML when I need to add, remove, move or change a block without copying a template or touching the module that owns it.
-
Adding a new block to an existing page
To put a banner or a custom block into the header, content or footer, you do not need to touch the module that owns the page. Add
<referenceContainer name="content"><block class="Vendor\Module\Block\Promo" name="promo.banner" template="Vendor_Module::promo.phtml"/></referenceContainer>to a layout file in your module for the right handle. No template copy, no preference, and it survives upgrades. -
Removing a default block from a page
You can hide a default block (page title, breadcrumbs, related products, the compare list) without disabling its module.
<referenceBlock name="page.main.title" remove="true"/>removes the page title on a CMS page, which I do on landing pages that print their own H1. This is better thandisplay:nonein CSS, because the block is never rendered at all. -
Reordering existing blocks
Move an existing block to a different container or change its position with
<move element="product.info.review" destination="product.info.main" before="product.info.price"/>. Useful when redesigning a PDP without rewriting every block: reviews above price, stock indicator under title, that kind of structural reshuffle. Works across containers, preserves the block’s arguments and template. -
Theme overrides without touching parent modules
Override layout in your theme by re-declaring the same handle file in
app/design/frontend/<vendor>/<theme>/<Module_Name>/layout/<handle>.xml. The theme XML loads last in the merge chain, so it wins. Lets you customise any Magento or extension page from a single theme directory: no module forks, no preference classes, survives module upgrades cleanly because the parent module never knows it was overridden.
Three traps that break layout XML silently
Every layout-XML bug I have been called in to debug came from one of these three mistakes. Each one fails silently: no error, just wrong output.
-
Using dashes in handle names instead of underscores
Magento layout handles use UNDERSCORES, not dashes:
catalog_product_viewnotcatalog-product-view,cms_page_view_id_what_is_hyva_themesnotcms-page-view-id-what-is-hyva-themes. Magento does not auto-convert. If you name your filecheckout-cart-index.xmlinstead ofcheckout_cart_index.xml, the file silently never loads: no error, no log entry, your changes just don’t appear. The URL slug uses dashes; the handle name uses underscores; mixing them up is the most common Magento layout mistake. -
Re-declaring a block with the same name as the parent
Each block name must be unique within the final merged layout tree. If you declare
<block name="product.info.review"/>in your module and Magento_Review already declared one with that exact name, Magento silently keeps both: you end up with two blocks rendering, causing duplicate review widgets, layout corruption, or mysterious extra HTML. Always namespace your block names:vendor.module.block.name, or use<referenceBlock>to modify the existing block instead of re-declaring it. -
Editing layout in pub/static/ instead of view/frontend/layout/
New Magento developers sometimes find a layout XML file in
pub/static/frontend/.../Module_Name/layout/and edit it directly. That directory is a build artefact: it gets wiped clean on everysetup:static-content:deployrun. The source of truth isapp/code/Vendor/Module/view/frontend/layout/or, for theme overrides,app/design/frontend/Vendor/theme/Module_Name/layout/. Edit source, then deploy. Treatpub/static/as read-only build output.
Magento Layout XML: frequently asked questions
-
What is the difference between layout XML and a phtml template?
Layout XML composes the page: it declares which blocks exist, what containers they live in, what order they render in, and which PHP class + template each block uses. The phtml template is the actual HTML markup that one block renders. Layout XML is structural and declarative ("here are the pieces and how they fit together"). Phtml is presentational and procedural ("here is the HTML for this one piece"). You change layout XML to add, remove, move, or reorder blocks; you change phtml to alter what a specific block looks like internally. -
How do I find the layout handle for a given page?
Enable template-path hints in admin under Stores > Configuration > Advanced > Developer > Debug, then load the page with ?ENABLE_HINTS=1: Magento shows every active handle in the page source. Or run the page through the Magento_Developer toolbar (Adobe Commerce only). Or check the controller action that handles the URL: every controller declares its handles via $resultPage->addHandle() or by convention from the route. For CMS pages, the handle is always cms_page_view_id_<identifier> (underscores, not dashes). -
Can I declare layout XML inside a CMS page?
Yes. Every CMS page has a Custom Layout Update field in the admin (Content > Pages > Edit > Custom Design tab). Paste a fragment like <referenceBlock name="page.main.title" remove="true"/> directly into that field: Magento merges it into the page-specific handle at render time. Useful for one-off page tweaks that do not justify a module deployment. The custom layout update wins over module XML for that single page only, which is exactly the right scope for marketing-led layout changes. -
Why does not my layout XML change apply?
Three usual suspects. First, cache: run bin/magento cache:clean layout block_html full_page (all three, not just one). Second, wrong handle name: check for dash-vs-underscore mistakes and confirm the file is in view/frontend/layout/ not view/adminhtml/layout/. Third, wrong module sequence: if another module declares the same block with higher merge priority, your change is overridden; declare a sequence dependency in your module.xml so your XML loads last, or move the override into the theme where it always wins. -
Does Hyvä still use layout XML?
Yes. Hyvä is a theme, not a layout-system replacement. It still uses the same handles (catalog_product_view, checkout_cart_index, cms_page_view_id_*), the same merge rules, the same <referenceBlock> and <referenceContainer> syntax. What Hyvä changes is what each block renders: Tailwind classes instead of Luma LESS, Alpine.js instead of KnockoutJS, but the composition layer above the templates is unchanged. Existing layout XML knowledge transfers 1:1 to Hyvä projects. -
How do I debug a missing block?
Start with bin/magento dev:source-theme:deploy to ensure no static-content cache is masking a missing source file. Then enable layout-hint mode via the URL parameter ?ENABLE_HINTS=1 to see the live tree on the page. Then check three things: (1) the handle is active on this page, (2) no upstream remove="true" is dropping the block, (3) the block class actually exists and DI compile has not been skipped. Magento also logs unresolved class references to var/log/exception.log: tail that file while loading the page to catch the actual error.
Stuck on a Magento layout XML problem?
Send the page URL and the XML snippet: I will diagnose handle mismatches, merge conflicts, missing block declarations, and theme override gotchas. 24-business-hour reply with a written fix and a quote if rework is needed.