Table of Contents & Menu
Navigation

Migrating Luma JavaScript and Templates

This guide helps you convert Luma JavaScript and templates to modern, Alpine.js-based Hyvä components.

From data-mage-init and require() to Inline Scripts

Hyvä inlines most JavaScript directly within `.phtml` templates, unlike Luma's external `data-mage-init` or `require()` scripts. This approach creates self-contained, reusable UI components and boosts page speed.

Process for Inlining Scripts

  1. Declare a new function within a <script> tag in your .phtml template. Give it a unique name to prevent global scope conflicts.

    <script>
        function initMyComponent() {
            // ...
        }
    
        document.addEventListener('alpine:init', () => {
            Alpine.data('initMyComponent', initMyComponent)
        });
    </script>
    

  2. Copy the contents of the original RequireJS module into this new function.

  3. Replace dependencies. Often, jQuery dependencies can be replaced with native JavaScript. Sometimes, this may involve inlining other external scripts.

  4. Call the new function based on its dependencies:

    • For code depending on customer section data, subscribe to the private-content-loaded event.
    • For code in the page header depending on the global window.hyva object, subscribe to DOMContentLoaded to ensure hyva is defined.
    • Otherwise, call the function inline after its definition.

From x-magento-template to Alpine.js x-for

Luma uses <script type="text/x-magento-template"> with mage/template and underscore.js to render data arrays. Hyvä replaces this with Alpine.js x-for directives, as it doesn't use those libraries.

Rendering Array Items with Alpine.js

Alpine.js x-for is the main directive for iterating arrays and rendering DOM elements.

<ul class="bundle items">
    <template x-for="option in selectedOptions">
        <li class="mb-2" x-show="option.products.length">
            <span class="text-base font-semibold" x-html="option.label"></span>
            <template x-for="product in option.products">
                <div><span x-html="product.qty"></span> x <span x-html="product.name"></span></div>
            </template>
        </li>
    </template>
</ul>

To access the index of the current iteration, use the (item, index) syntax:

<template x-for="(product, index) in products" :key="index">
    <!-- ... -->
</template>

For more details, see the Alpine.js x-for documentation.

From jQuery data() to Native dataset

jQuery's $(selector).data() reads data-* attributes, caching values and auto-parsing JSON. When migrating to vanilla JavaScript, you must manually replicate these behaviors if needed.

The native equivalent, element.dataset, offers a live view of attributes. If an attribute is removed from the DOM, its value is no longer accessible via dataset.

Reading Data Attributes

Consider this element: <div id="my-id" data-example='{"name": "Alice"}'></div>

jQuery Way:

// jQuery reads, parses, and caches the value.
$('#my-id').data('example');

Native JavaScript / Alpine.js Way: To cache a value (e.g., before removing the attribute), store it manually on a custom element property.

const element = document.getElementById('my-id');

// Manually parse JSON and store it on a custom property
element.__example = JSON.parse(element.dataset.example);

// The value is now cached even if the attribute is removed
element.removeAttribute('data-example');
console.log(element.__example); // => {name: "Alice"}

Native Equivalents for Underscore Functions

Here are native JavaScript equivalents for common Underscore.js utility functions.

Underscore Native JavaScript Equivalent
_.isObject(x) const isObject = x => x === Object(x);
_.isArray(x) Array.isArray(x)
_.has(x, p) const has = (x, p) => x === Object(x) && x.hasOwnProperty(p);
_.isEqual(x, y) JSON.stringify(x) === JSON.stringify(y) (for simple objects/arrays)

Native Equivalents for jQuery Functions

Most jQuery functions have native JavaScript equivalents. For a comprehensive guide, see:

You Might Not Need jQuery