Alpine.js vervangt jQuery in Hyvä — en terecht. Het weegt 15 kB, werkt declaratief en past perfect bij de Magento component-architectuur. Een praktische tutorial voor developers die de overstap maken.
Alpine.js voor Magento developers: van jQuery naar modern JavaScript
15 kilobyte. Dat is wat Alpine.js weegt. Vergelijk dat met jQuery's 87 kB of Magento's volledige RequireJS-bundel die gemakkelijk 300 kB haalt. Alpine.js is de standaard JavaScript-laag in Hyvä Magento themes — en als je gewend bent aan jQuery of Knockout.js, is de leercurve mild.
Dit artikel legt uit hoe Alpine.js werkt in een Magento/Hyvä context. Met concrete voorbeelden die je direct kunt gebruiken.
Wat je leert in dit artikel
- Waarom Alpine.js past bij Magento's component-architectuur
- De kernprincipes:
x-data,x-on,x-show,x-bindenx-model - Hoe je Alpine.js componenten registreert en hergebruikt in Hyvä
- Interactie met de Hyvä event bus en GraphQL
- Praktische patronen: tabbladen, modals, quantity selectors
Waarom Alpine.js en niet React of Vue?
React en Vue zijn frameworks voor applicaties. Ze beheersen de volledige DOM, vereisen een build-stap en voegen complexiteit toe die de meeste Magento-componenten niet rechtvaardigen.
Alpine.js is een bibliotheek voor interactiviteit op HTML-niveau. Je schrijft je HTML in een Magento .phtml template, voegt een paar x--attributen toe en Alpine.js beheert de state. Geen virtual DOM, geen component lifecycle hooks, geen Webpack-configuratie.
Voor Hyvä is dat perfect. Hyvä gebruikt Alpine.js voor kleine, scherpe componenten: een menu dat opent, een modal die verschijnt, een quantity selector die telt. Grote datatransformaties gaan via Tailwind + PHP, niet via JavaScript.
Vergelijking met Knockout.js (Magento's eerdere standaard):| Aspect | Knockout.js | Alpine.js |
|---|---|---|
| Gewicht | ~60 kB | ~15 kB |
| Syntaxis | data-bind="text: naam" | x-text="naam" |
| Leesbaarheid | Matig | Hoog |
| Hyvä-ondersteuning | Geen | Standaard |
| Two-way binding | Ja | Ja (x-model) |
De kern van Alpine.js: x-data
x-data is het startpunt van elk Alpine component. Het definieert de state die dat component beheert.
<!-- Eenvoudige teller — basis van elk Alpine component -->
<div x-data="{ aantal: 1 }">
<button x-on:click="aantal--" :disabled="aantal <= 1">-</button>
<span x-text="aantal"></span>
<button x-on:click="aantal++">+</button>
</div>
De state leeft binnen het element en zijn children. Buiten dat element weet Alpine niets van aantal. Dat is bewuste encapsulatie.
x-data als functie registreren
Voor herbruikbare componenten registreer je de state als een functie via Alpine.data(). In Hyvä doe je dat in een .js-bestand dat je inlaadt via .
// Registreer een herbruikbaar component
document.addEventListener('alpine:init', () => {
Alpine.data('quantitySelector', (initialValue = 1) => ({
aantal: initialValue,
min: 1,
max: 99,
verlaag() {
if (this.aantal > this.min) this.aantal--;
},
verhoog() {
if (this.aantal < this.max) this.aantal++;
},
// Voeg toe aan winkelwagen via Hyvä event bus
voegToeAanWinkelwagen(productId) {
window.dispatchEvent(new CustomEvent('add-to-cart', {
detail: { product_id: productId, qty: this.aantal }
}));
}
}));
});
<!-- Gebruik in je Hyvä .phtml template -->
<div x-data="quantitySelector(<?= (int) $product->getMinSaleQty() ?>)">
<button x-on:click="verlaag()" :disabled="aantal <= min">-</button>
<input type="number" x-model="aantal" :min="min" :max="max">
<button x-on:click="verhoog()">+</button>
<button x-on:click="voegToeAanWinkelwagen(<?= (int) $productId ?>)">
Toevoegen
</button>
</div>
Veelgebruikte directives
x-show en x-if
x-show verbergt een element met CSS (display: none). Het element blijft in de DOM. x-if verwijdert het element volledig.
<!-- x-show: element blijft in DOM, wordt verborgen/getoond -->
<div x-data="{ open: false }">
<button x-on:click="open = !open">Filter tonen</button>
<div x-show="open" x-transition>
<!-- Filteropties -->
</div>
</div>
<!-- x-if: element wordt verwijderd/aangemaakt -->
<template x-if="gebruikerIsIngelogd">
<div>Welkom terug!</div>
</template>
Gebruik x-show als het element snel moet verschijnen/verdwijnen. Gebruik x-if als het element zwaar is en niet gerenderd moet worden als het niet nodig is.
x-bind voor dynamische attributen
x-bind (of de verkorte :) koppelt een JavaScript-expressie aan een HTML-attribuut.
<div x-data="{ actieveTab: 'beschrijving' }">
<!-- Dynamische class op basis van actieve tab -->
<button
:class="actieveTab === 'beschrijving' ? 'border-b-2 border-blue-600 font-semibold' : 'text-gray-500'"
x-on:click="actieveTab = 'beschrijving'"
>
Beschrijving
</button>
<button
:class="actieveTab === 'specs' ? 'border-b-2 border-blue-600 font-semibold' : 'text-gray-500'"
x-on:click="actieveTab = 'specs'"
>
Specificaties
</button>
<div x-show="actieveTab === 'beschrijving'">
<?= $block->getDescription() ?>
</div>
<div x-show="actieveTab === 'specs'">
<?= $block->getSpecifications() ?>
</div>
</div>
x-model voor formulieren
x-model synchroniseert een input-waarde met de Alpine-state in beide richtingen.
<div x-data="{ zoekterm: '', resultaten: [] }">
<input
type="text"
x-model="zoekterm"
x-on:input.debounce.300ms="zoekProducten()"
placeholder="Zoek een product..."
>
<template x-for="product in resultaten" :key="product.id">
<div x-text="product.naam"></div>
</template>
</div>
Het .debounce.300ms modifier op x-on:input zorgt dat de zoekopdracht pas na 300ms stilstand wordt uitgevoerd — een ingebouwde Alpine-functionaliteit die in jQuery aparte implementatie vereiste.
Hyvä event bus integratie
Hyvä gebruikt een custom event bus gebaseerd op browser CustomEvent. Alpine-componenten communiceren via window.dispatchEvent en window.addEventListener.
// Verstuur een event vanuit Alpine component
document.addEventListener('alpine:init', () => {
Alpine.data('productCard', (productId) => ({
bezig: false,
async voegToe(qty) {
this.bezig = true;
// Hyvä's native add-to-cart event
window.dispatchEvent(new CustomEvent('add-to-cart', {
detail: {
product_uid: btoa('SimpleProduct/' + productId),
entered_options: [],
selected_options: [],
quantity: qty
}
}));
// Luister naar bevestiging
window.addEventListener('cart-item-added', () => {
this.bezig = false;
}, { once: true });
}
}));
});
<div x-data="productCard(<?= (int) $productId ?>)">
<button
x-on:click="voegToe(1)"
:disabled="bezig"
:class="bezig ? 'opacity-50 cursor-not-allowed' : ''"
>
<span x-show="!bezig">In winkelmand</span>
<span x-show="bezig">Bezig...</span>
</button>
</div>
Modal-patroon
Modals zijn een klassiek gebruik van Alpine.js. Met x-teleport kun je de modal buiten de huidige DOM-boom plaatsen — handig om z-index problemen te vermijden.
<!-- Trigger knop -->
<div x-data="{ modalOpen: false }">
<button x-on:click="modalOpen = true">Meer info</button>
<!-- Teleport naar body-niveau -->
<template x-teleport="body">
<div
x-show="modalOpen"
x-transition.opacity
class="fixed inset-0 bg-black bg-opacity-50 z-50 flex items-center justify-center"
x-on:click.self="modalOpen = false"
x-on:keydown.escape.window="modalOpen = false"
>
<div class="bg-white rounded-lg p-6 max-w-lg w-full mx-4">
<button
x-on:click="modalOpen = false"
class="float-right text-gray-400 hover:text-gray-600"
>
✕
</button>
<h2>Productinformatie</h2>
<?= $block->getProductInfo() ?>
</div>
</div>
</template>
</div>
Van jQuery naar Alpine: het mentale model
jQuery denkt in acties: "klik op element X, doe Y met element Z." Alpine denkt in state: "als state A waar is, toon element B."
| jQuery-patroon | Alpine-equivalent |
|---|---|
$(el).show() | x-show="conditie" |
$(el).addClass('active') | :class="{ active: conditie }" |
$(el).text(waarde) | x-text="waarde" |
$(el).val() | x-model="waarde" |
$(el).on('click', fn) | x-on:click="fn()" |
$.ajax() | fetch() in een Alpine-methode |
De grootste verschuiving is dat je stopt met het manipuleren van de DOM en begint met het beheren van state. Alpine zorgt dan automatisch voor de DOM-updates.
Veelgemaakte fouten
1. State buiten x-data proberen te lezen. Alpine-state is scoped. Wil je state delen tussen componenten, gebruik dan de Hyvä event bus of Alpine's$store.
2. Zware operaties in x-data stoppen. API-calls en complexe berekeningen horen in methoden, niet in de initiële state-definitie. Anders blokkeert de pagina bij het laden.
3. x-if en x-show door elkaar gebruiken. x-if vereist een wrapper. x-show werkt op elk element. Gebruik x-if spaarzaam — het is zwaarder dan x-show.
4. Alpine.js laden voor de DOM klaar is. Hyvä laadt Alpine.js correct via defer. Als je zelf Alpine toevoegt, zorg dat het na de DOM geladen is of gebruik de alpine:init event.
5. Inline x-data herhalen voor hetzelfde component type. Als je op een categoriepagina twintig productkaarten hebt, elk met dezelfde inline x-data-definitie, voeg je onnodige overhead toe. Registreer herbruikbare componenten één keer via Alpine.data() en verwijs ernaar.
Alpine.js en $store: globale state
Voor state die meerdere componenten moeten delen — winkelwageninhoud, gebruikersstatus, actieve filters — biedt Alpine's $store een oplossing.
document.addEventListener('alpine:init', () => {
Alpine.store('winkelwagen', {
aantalItems: 0,
totaal: 0,
voegToe(prijs) {
this.aantalItems++;
this.totaal += prijs;
}
});
});
<!-- Toegankelijk vanuit elk Alpine component op de pagina -->
<span x-text="$store.winkelwagen.aantalItems"></span>
In Hyvä gebruik je voor winkelwagen-state bij voorkeur de ingebouwde event bus, omdat Hyvä's eigen cart-logica al via events communiceert. $store is meer geschikt voor UI-state zoals actieve filters of navigatiestatus.
Conclusie
Alpine.js is geen vervanging van React of Vue voor complexe applicaties. Het is een gereedschap voor component-level interactiviteit in server-rendered HTML — precies wat Magento en Hyvä nodig hebben.
De leercurve is klein als je jQuery kent. Het mentale model verschilt, maar de syntaxis is intuïtief. Na een dag werken met Alpine, wil je niet meer terug naar jQuery's imperatieve DOM-manipulatie.
Bekijk meer over Hyvä Magento development of lees onze prestatie-vergelijking van Hyvä vs Luma. De officiële Alpine.js documentatie is een uitstekend startpunt voor developers die het framework willen leren.
Werken jullie met Hyvä en wil je weten hoe Alpine.js in jouw theme past? Neem contact op met Coding.nl.
Veelgestelde vragen
Kan ik Alpine.js gebruiken in een standaard Luma-theme?Technisch wel, maar Luma gebruikt RequireJS en Knockout.js als standaard. Alpine.js toevoegen aan Luma creëert een dubbele JavaScript-laag. Zinvoller is het om over te stappen naar Hyvä, waar Alpine.js de standaard is.
Hoe debug ik Alpine.js componenten?Installeer de Alpine.js DevTools browser-extensie. Die toont de state van elk component live in de browser. Handig voor het debuggen van state-updates en event-handlers.
Kan Alpine.js GraphQL-calls uitvoeren?Ja. Alpine heeft geen ingebouwde HTTP-client, maar je gebruikt gewoon de native fetch API. Hyvä heeft bovendien een hyva.fetch() helper die CSRF-tokens automatisch meestuurt.
Nee. Voor een volledige Single Page Application gebruik je React, Vue of Svelte. Alpine.js is bedoeld voor progressieve verrijking van server-rendered HTML.
Wat is het verschil tussen Alpine.js v2 en v3?Hyvä gebruikt Alpine.js v3. De grootste verschillen: x-data als functie registreer je nu via Alpine.data() in de alpine:init event, en x-teleport is nieuw in v3. Zorg dat je documentatie gebruikt die overeenkomt met v3.

Geschreven door Ruthger Idema
15+ jaar ervaring in e-commerce development. Gespecialiseerd in Magento, Shopify en Laravel maatwerk.
Meer over ons team →