Skip to content
Hyvä Theme · 6 min read · Published Aug 22, 2026

How to Create a Custom Module in Hyvä Theme

The Hyvä Theme has become one of the most popular frontend solutions for Magento 2 due to its speed and simplicity. Unlike the default Luma theme, Hyvä removes heavy JavaScript dependencies and uses Alpine.js and Tailwind CSS for a lightweight frontend.

In this step-by-step guide, you will learn how to create a custom module/extension that works properly with the Hyvä Theme.

New to Hyvä? Start with our Hyvä Theme answers hub for the fundamentals before diving into custom module development.

Get an instant AI summary of this post

Prerequisites

Before starting, make sure you have:

  • Magento 2.4.4 or higher installed
  • Hyvä Theme installed and working
  • Basic knowledge of Magento module development
  • Access to terminal/CLI

This guide is written against the current Hyvä module conventions, including the hyva_ layout handle pattern covered later. If you’re on an older Hyvä release, check the official Hyvä documentation to confirm the same conventions apply to your version.

Understanding Hyvä Compatibility

Hyvä does not use RequireJS or KnockoutJS like the Luma theme. Instead, it relies on:

  • Alpine.js
  • Tailwind CSS
  • PHP templates

Hyvä also doesn’t support LESS-based styling the way Luma does. Tailwind CSS utility classes are the standard approach for all frontend styling.

Because of this, many traditional Magento frontend modules require adjustments to work with Hyvä.

One distinction worth knowing: if you’re making an existing third-party module Hyvä-compatible, the convention is to prefix the compatibility module with Hyva_, for example Hyva_VendorModuleName. That’s different from what we’re doing in this guide. Since we’re building a brand-new custom module rather than patching an existing one, it just uses your own vendor namespace with no special prefix.

There’s a related difference in how the two are registered. A third-party compatibility module usually needs to register itself using frontend/di.xml, since it’s overriding existing templates without touching layout XML directly. A custom module like the one you’re building here doesn’t need that, it has its own dedicated files and isn’t overriding anything that already exists.

Step 1: Create Module Folder Structure

Create your custom module inside app/code:

app/code/Vendor/HyvaCustom/

Basic structure:

Vendor/
└── HyvaCustom/
    ├── registration.php
    ├── etc/module.xml
    └── view/frontend/
Magento 2 custom module directory structure.

Step 2: Create registration.php

<?php
use Magento\Framework\Component\ComponentRegistrar;

ComponentRegistrar::register(
    ComponentRegistrar::MODULE,
    'Vendor_HyvaCustom',
    __DIR__
);

This file registers your module with Magento. Without it, Magento has no way of knowing your module exists at all, no matter how correctly the rest of your files are set up. The module name you pass here, Vendor_HyvaCustom in this example, needs to match exactly what you use in module.xml in the next step.

Step 3: Create module.xml

File: etc/module.xml

<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd">
    <module name="Vendor_HyvaCustom" setup_version="1.0.0"/>
</config>

Step 4: Enable the Module

Run the following commands:

bin/magento setup:upgrade
bin/magento cache:flush

If everything is correct, your module is now active. You can confirm this by running bin/magento module:status Vendor_HyvaCustom, it should show up under “Enabled modules.”

If it doesn’t show up, check two things first. Make sure your namespace and module name match exactly across registration.php and module.xml, a small typo in either file is the most common reason a module silently fails to register. Also check var/log/system.log for any errors thrown during setup:upgrade, Magento usually tells you exactly what went wrong.

Step 5: Create Hyvä-Compatible Template

In Hyvä, frontend templates are simpler because there is no KnockoutJS. Create a template file:

view/frontend/templates/example.phtml
<?php
/** @var \Magento\Framework\View\Element\Template $block */
?>

<div x-data="{ open: false }" class="p-4 bg-neutral-100">
    <button @click="open = !open" class="bg-blue-500 text-white px-4 py-2">
        Toggle Message
    </button>

    <div x-show="open" class="mt-2">
        Hello from Hyvä Custom Module!
    </div>
</div>

Built using lightweight JavaScript (Alpine.js) and styled with Tailwind CSS, making it fast, simple, and fully compatible with Hyvä Theme.

Step 6: Create Layout XML

Important: Use the hyva_ Layout Handle (Hyvä Best Practice)

In Hyvä Theme development, you should use the hyva_ prefix for layout XML files when your changes are intended only for the Hyvä frontend.

This ensures your customization does not affect the Luma theme or other frontends and keeps your module clean and compatible.

When to Use the hyva_ Prefix

  • When the layout update is Hyvä-specific
  • When your template uses Alpine.js or Tailwind
  • When you want to avoid affecting Luma
  • When building Hyvä compatibility modules

When NOT to Use It

  • If the layout must work for both Luma and Hyvä
  • If the block is backend/admin related
  • If the module is completely frontend-agnostic

Step Update: Create Hyvä-Specific Layout XML

Instead of using:

view/frontend/layout/default.xml

For Hyvä-specific changes, create:

view/frontend/layout/hyva_default.xml

This layout will only load when the Hyvä Theme is active.

<?xml version="1.0"?>
<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd">

    <body>
        <referenceContainer name="content">
            <block class="Magento\Framework\View\Element\Template"
                   name="hyva.custom.block"
                   template="Vendor_HyvaCustom::example.phtml"/>
        </referenceContainer>
    </body>
</page>
  • ✅ Loads only in Hyvä
  • ✅ Keeps Luma untouched
  • ✅ Recommended by the Hyvä team

Official reference: Hyvä Layout Handles Documentation

Step 7: Deploy Static Content

bin/magento setup:static-content:deploy -f
bin/magento cache:flush

Now check your frontend. You should see the “Toggle Message” button, and clicking it should reveal the hidden text without a page reload, that’s Alpine.js handling the interaction entirely in the browser.

If nothing shows up, check these first:

  • Confirm the block actually renders by viewing the page source and searching for hyva.custom.block
  • Make sure you’re viewing the page with the Hyvä theme active, not Luma, since hyva_default.xml only loads for Hyvä
  • Open your browser console for JavaScript errors, a missing or misconfigured Alpine.js build is the most common cause of a silent failure

Here’s what it looks like once it’s working. Before clicking, only the button is visible:

Hyvä custom module Toggle Message button before being clicked, on a live Magento storefront
The custom Hyvä module before the toggle is clicked.

After clicking, Alpine.js reveals the message instantly, no page reload, no extra JavaScript library:

Hyvä custom module showing the revealed message after the Toggle Message button is clicked
The same module after clicking, with Alpine.js revealing the message.

Step 8: Build Your Tailwind CSS

Step 8: Build Your Tailwind CSS

Running setup:static-content:deploy and cache:flush is not enough on its own. Those commands copy static assets, they don’t compile Tailwind. If you skip this step, your new template’s classes simply won’t exist in the compiled stylesheet, and nothing will render even though the module works perfectly.

How you do this depends on which Tailwind version your Hyvä theme is running.

Check Your Tailwind Version First

composer show hyva-themes/magento2-default-theme

Hyvä Theme 1.4 and above use Tailwind CSS v4. Anything below that is on Tailwind v3. The build process is different for each, so confirm this before proceeding.

Tailwind v3: Add Your Module Path

In your theme’s web/tailwind/tailwind.config.js, add your module’s template path to the content array:

module.exports = {
  content: [
    '../../**/*.phtml',
    '../../../../../../code/Vendor/HyvaCustom/**/*.phtml',
  ],
}

Then build from the theme’s tailwind directory:

cd app/design/frontend/Vendor/your-theme/web/tailwind
npm install
npm run build

Tailwind v4: Add a @source Directive

Tailwind v4 dropped tailwind.config.js in favor of CSS-first configuration. Instead of editing a JS config, open web/tailwind/tailwind-source.css and add a @source line pointing to your module:

/* My custom module templates */
@source "../../../../../../code/Vendor/HyvaCustom/**/*.phtml";

Then build the same way:

cd app/design/frontend/Vendor/your-theme/web/tailwind
npm install
npm run build

The build script runs Hyvä’s source generation first, then compiles tailwind-source.css into web/css/styles.css.

Redeploy After Building

Once the CSS is compiled, redeploy and clear the cache so Magento picks up the new file:

bin/magento setup:static-content:deploy -f
bin/magento cache:flush

Best Practices for Hyvä Modules

  • Avoid RequireJS and jQuery
  • Prefer Alpine.js for interactivity
  • Use Tailwind utility classes
  • Keep templates lightweight
  • Follow Magento coding standards

Common Mistakes to Avoid

  • Using KnockoutJS in Hyvä templates
  • Adding RequireJS dependencies
  • Forgetting to deploy Tailwind build
  • Overriding unnecessary core files

FAQ

Does this approach also work with Hyvä Checkout?

Yes, the same module structure and Alpine.js approach apply. Hyvä Checkout has its own layout conventions for checkout-specific steps, but the core registration, layout XML, and templating pattern shown here work the same way.

Do I need to know Tailwind CSS and Alpine.js before starting?

Not for the module registration steps, those are standard Magento. For the template itself, basic familiarity helps: Alpine.js handles interactivity with simple attributes like x-data and x-show, and Tailwind provides utility classes for styling. Both are easier to pick up than RequireJS and KnockoutJS, which is part of why Hyvä feels lighter to work with.

Can I convert an existing Luma module to Hyvä instead of starting from scratch?

Usually, yes, though the amount of rework depends on how much your existing module leans on RequireJS or KnockoutJS. The module registration and layout XML files carry over largely unchanged. The templates are where the real work happens, since anything relying on KnockoutJS bindings needs to be rebuilt with Alpine.js.

Final Thoughts

Creating a custom module for the Hyvä Theme is actually simpler than traditional Magento frontend development. Because Hyvä removes heavy dependencies, your modules become faster, cleaner, and easier to maintain.

If you are building new Magento functionality, always consider Hyvä compatibility from the beginning to avoid rework later.

Creating a custom module for the Hyvä Theme is actually simpler than traditional Magento frontend development. Because Hyvä removes heavy dependencies, your modules become faster, cleaner, and easier to maintain.

If you are building new Magento functionality, always consider Hyvä compatibility from the beginning to avoid rework later.

Stuck on a Hyvä compatibility issue?

We build and fix custom Hyvä modules every week. If something isn’t rendering right or a third-party extension won’t cooperate, we can take a look.

Talk to a Hyvä Specialist
About the author
Priya Patel
Priya Patel
Magento Developer, Stagebit

Passionate Magento developer with expertise in custom module development, Hyvä themes, and Adobe Commerce. Dedicated to creating high-quality, maintainable solutions that improve performance and user experience.

Same-day response

Free Consultation

Directly with our experts

30-min call. No commitment. Tell us your problem, we'll tell you how to fix it.

Book Free Consultation or call +91 84601 36159
Share:
𝕏in🔗Free Audit