ddtcorex

01 Sept 2026 · 2 min read

Layout XML and PHTML overrides in Hyva

How Magento 2 assembles pages from layout XML, and how Hyva overrides differ from Luma — same composition model, Tailwind markup instead of Knockout, and a smaller template surface.

Part 6 of the Hyva series — building the core storefront. Implementation-focused.

The composition model does not change

Magento assembles every page from layout/*.xml files: containers hold blocks, blocks render through .phtml templates. Hyva keeps this model exactly. What changes is what the templates contain — Tailwind classes instead of Luma markup, Alpine instead of Knockout — and the fact that you override far fewer of them, because the base theme already covers most storefront surfaces.

A typical override still moves a block, adds a child, or swaps a template:

<referenceContainer name="content">
    <block class="Vendor\Module\Block\Promo" name="promo.banner"
           template="Vendor_Module::promo.phtml" />
</referenceContainer>

The declarative approach means you reshape pages without touching controllers — identical to Luma.

How Hyva overrides differ

In Luma, a storefront customization often meant editing a deep Less file, adding a requirejs-config entry, and binding a uiElement. In Hyva, the same customization is usually:

  1. A view/frontend/templates PHTML override using Tailwind classes.
  2. A small Alpine x-data for any interactivity.
  3. Optionally a PHP view model for display logic.

There is no JavaScript module to register and no binding engine to boot. The template is the unit of work, and it is plain HTML with utility classes.

A concrete override

Suppose you want a custom promo banner on the category page. In your child theme:

Magento_Catalog/
  view/frontend/templates/
    category/promo.phtml

Reference it from a layout update:

<referenceContainer name="content">
    <block class="Magento\Framework\View\Element\Template"
           name="category.promo"
           template="Magento_Catalog::category/promo.phtml"
           after="-" />
</referenceContainer>

And the template itself is just markup:

<section class="my-8 rounded-2xl bg-brand/10 p-6 text-center">
  <p class="text-lg font-semibold text-brand-dark">Free shipping this week</p>
</section>

No Knockout, no RequireJS, no theme compile step beyond Tailwind. The banner appears on the category page and the build stays small.

Containers, blocks, and the Hyva base

Hyva ships a complete set of base templates per module. You only override the ones you need to change. This is why a migration's template count is bounded — most pages render from the base theme untouched, and your child theme holds only the deltas.

When you do override, prefer the narrowest scope:

  • Override the specific template, not the whole module's templates directory.
  • Use layout XML to reorder and remove before reaching for a new block.
  • Keep interactivity in a small x-data component rather than a global script.

A note on the page weight

Because Hyva templates avoid the Knockout/customer-data machinery, the pages you assemble this way ship a fraction of the JavaScript Luma would. The layout XML is the same mental model you already have; the template body is what got lighter.

The next post goes one level deeper into that interactivity: the Alpine patterns that replace Knockout widgets.

Next in this series: Alpine.js patterns that replace Knockout — /blog/magento2-hyva-alpine-patterns

magento2layouthyvafrontend