Table of Contents & Menu
Navigation

Writing Alpine v2 and v3 Compatible Code

Extension developers often need to support both Alpine.js v2 and v3.

It's straightforward to write Alpine components that work with both versions. This guide outlines key considerations for maintaining compatibility across Hyvä versions.

x-spread and x-bind

Alpine.js v2 uses x-spread, while v3 uses x-bind. To ensure compatibility, include both attributes:

<div
    class="fixed inset-0 z-30 flex items-center justify-center text-left"
    x-spread="overlay()"
    x-bind="overlay()"
    x-cloak
>
    ... the other modal code here ...
</div>

Using $el

In Alpine v2, $el always referred to the component's root DOM element. In v3, it refers to the DOM element of the currently evaluated binding.

For v2 and v3 compatibility, only use $el on the component's base element. To reference other elements, use x-ref or querySelector():

<div x-data="{ result: 0 }">
    <button
        x-ref="btn"
        data-value="6"
        @click="result += parseInt($refs.btn.dataset.value)"
    >...<button>
</div>

Using $root

In Alpine v2, $root is unavailable. The recommended approach is to avoid using it. Alternatively, alias it in an init method for v2 compatibility:

<div
    x-data="{
        init(root) {
            if (!this.$root) this.$root = root;
        }
    }"
    x-init="init($el)"
>
    ...
</div>

Using x-init

Alpine.js v2 requires explicit x-init calls for initialization functions. In v3, an init method is called automatically. For v2 compatibility, always explicitly use x-init="init" instead of relying on implicit calls.

@click.away or @click.outside

Alpine v2's click.away modifier was renamed to click.outside in v3. To support both versions, use both modifiers:

<div @click.away.outside="open = false">
    ...
</div>

Using Alpine plugins

Alpine.js v3 includes many native plugins and offers an improved API for custom plugin development.

For cross-version compatibility, avoid plugins unless they are available for both Alpine versions. The intersect plugin is an exception: it's native to v3, and Hyvä provides a v2 backport, making x-intersect="..." safe to use universally.

For other plugins, you'll likely need to backport them to v2 and manage version loading (see below).

Rendering templates depending on the Alpine version

The etc/hyva-libraries.json theme file defines which library versions to load. Use the \Hyva\Theme\ViewModel\ThemeLibrariesConfig::getVersionIdFor view model method to retrieve these versions. This determines both the Alpine.js and intersect plugin versions for a theme:

<?php

/** @var Template $block */
/** @var ViewModelRegistry $viewModels */

use Hyva\Theme\Model\ViewModelRegistry;
use Hyva\Theme\ViewModel\ThemeLibrariesConfig;
use Magento\Framework\View\Element\Template;

$themeLibrary = $viewModels->require(ThemeLibrariesConfig::class);
$version = $themeLibrary->getVersionIdFor('intersect-plugin')
    ?: $themeLibrary->getVersionIdFor('alpine')
    ?: '2';

?>
<?= /** @noEscape */ $block->fetchView($block->getTemplateFile("Hyva_Theme::page/js/plugins/v${version}/intersect.phtml")) ?>

This code first checks for a specific intersect-plugin version. If not found, it checks for an Alpine.js version. If neither is specified (e.g., no `etc/hyva-libraries.json` file), it defaults to version `2`. The determined version then constructs the template file path for rendering.

Apply this pattern to render custom JavaScript based on the theme's Alpine version.

Warning if the Alpine version is too old

If your module only supports a single Alpine.js version, clearly state this in the module's README and documentation. Note that a Magento instance might use different Alpine versions across store views with different themes.

To declare a minimum Alpine version dependency, add a child block to the require-alpine-v3 block using layout XML:

<body>
    <referenceBlock name="require-alpine-v3">
        <block name="My_Module"/>
    </referenceBlock>
</body>

If this module is used with a theme running an older Alpine version, a warning will be logged to the browser console:

The current Alpine.js version is 2.8.2, but version >= 3 is required by the following extensions:
[
    'My_Module'
]

The child block's name should match your module name, as it appears in the error message.

This approach only declares a minimum version

There's no generic way to warn if a higher Alpine version is present (e.g., requiring v2 but v3 is used).