Hyvä UI levert componenten die werken zonder build-step, zonder Webpack-config en zonder npm-scripts. De reden: Alpine.js als enige JavaScript-laag, precies zo ingezet als Hyvä het bedoeld heeft. In dit artikel laten we zien welke patronen terugkomen, hoe reactivity werkt en waar de grenzen liggen.
Alpine.js patterns in Hyvä UI
Hyvä UI levert componenten die werken zonder build-step, zonder Webpack-config en zonder npm-scripts. De reden: Alpine.js als enige JavaScript-laag, precies zo ingezet als Hyvä het bedoeld heeft. In dit artikel laten we zien welke patronen terugkomen, hoe reactivity werkt en waar de grenzen liggen.
Wat Alpine.js doet in Hyvä UI
Alpine.js is geen framework in de klassieke zin. Het is een declaratieve laag die je direct in HTML schrijft. Geen virtual DOM, geen component-files, geen bundler vereist.
Hyvä UI bouwt daar consequent op voort. Elke component is een stuk HTML met Alpine-attributen. Je kopieert de markup, plakt hem in je template en het werkt.
De vier attributen die je het meest tegenkomt:
x-data— definieert de lokale state van een componentx-show/x-if— conditionele rendering@click/@keydown— event-handlersx-bind/:class— dynamische classes en attributen
Dat is het. Geen reducers, geen stores, geen lifecycle-hooks tenzij je ze zelf nodig hebt.
x-data: lokale state per component
x-data is het startpunt van elke interactieve component in Hyvä UI. Je definieert er een JavaScript-object mee dat lokaal is aan dat element en zijn children.
Voorbeeld: een accordion die open en dicht klapt.
<div x-data="{ open: false }">
<button @click="open = !open" class="w-full text-left font-semibold py-3">
Vraag: wat kost Hyvä UI?
<span :class="open ? 'rotate-180' : ''" class="transition-transform inline-block">▼</span>
</button>
<div x-show="open" x-transition class="text-gray-600 pb-4">
Eenmalig ongeveer 250 euro, per-ontwikkelaar/projectlicentie. Geen abonnement.
</div>
</div>
Dit is precies hoe de accordion-component in Hyvä UI werkt. Geen externe file, geen import. State leeft in de markup zelf.
Voor complexere componenten, zoals een modal met meerdere triggers, verplaats je de logica naar een x-data-functie:
<div x-data="modal()">
<button @click="open()">Open modal</button>
<div x-show="isOpen" @click.away="close()" class="fixed inset-0 bg-black/50 flex items-center justify-center">
<div class="bg-white rounded-lg p-6 max-w-md w-full">
<button @click="close()" class="float-right text-gray-400 hover:text-gray-600">✕</button>
<slot></slot>
</div>
</div>
</div>
<script>
function modal() {
return {
isOpen: false,
open() { this.isOpen = true },
close() { this.isOpen = false },
}
}
</script>
De functie modal() definieer je eenmalig en hergebruik je overal. Geen ES-modules nodig.
x-show vs x-if: wanneer gebruik je wat?
Een veelgestelde vraag. Het verschil is klein maar relevant voor performance.
| Attribuut | Werking | Gebruik wanneer |
|---|---|---|
x-show | Zet display: none via CSS | Element wordt frequent getoond/verborgen |
x-if | Voegt element toe/verwijdert het uit de DOM | Element is zelden zichtbaar of bevat zware markup |
Hyvä UI gebruikt x-show voor modals, dropdowns en accordions. De reden: CSS-transities werken alleen als het element in de DOM aanwezig is. Met x-if vervalt x-transition.
Voor product cards met lazy-loaded afbeeldingen of content die zelden wordt getoond, is x-if de juiste keuze. Minder DOM, betere Lighthouse-score.
Events en component-communicatie
Alpine.js heeft een ingebouwd event-systeem via $dispatch en @. Hyvä UI gebruikt dit voor communicatie tussen componenten die geen gedeelde parent hebben.
Voorbeeld: een "voeg toe aan winkelwagen"-knop die een toast-notificatie triggert elders op de pagina.
<!-- Knop (ergens in product card) -->
<button @click="$dispatch('cart:added', { name: 'Product X' })">
In winkelwagen
</button>
<!-- Toast (in header of body) -->
<div
x-data="{ show: false, message: '' }"
@cart:added.window="show = true; message = $event.detail.name; setTimeout(() => show = false, 3000)"
x-show="show"
class="fixed bottom-4 right-4 bg-green-600 text-white px-4 py-2 rounded shadow-lg"
>
<span x-text="message"></span> toegevoegd aan winkelwagen.
</div>
.window op de event-listener zorgt dat het event op window-niveau wordt gevangen. Zo communiceren twee losstaande Alpine-instanties zonder gedeelde state of parent-component.
Dit patroon zie je in de Hyvä UI navigatie- en notificatie-componenten. Het houdt de structuur flat en vermijdt prop-drilling.
Reactivity zonder build-step
Het krachtige van Alpine.js is dat reactivity automatisch werkt. Je muteert state, de DOM updatet. Geen setState, geen ref, geen watcher nodig voor simpele gevallen.
<div x-data="{ count: 0, doubled: 0 }" x-init="$watch('count', val => doubled = val * 2)">
<button @click="count++">+</button>
<p>Count: <span x-text="count"></span></p>
<p>Dubbel: <span x-text="doubled"></span></p>
</div>
$watch is een Alpine-magic property die een waarde observeert en een callback aanroept bij wijziging. Handig voor afgeleide waarden, validatie of API-calls die afhangen van state.
Wij zien bij klanten dat dit patroon 80% van de interactieve frontend-behoeften dekt. Tabs, filters, toggles, accordions, modals: allemaal oplosbaar met x-data + x-show + @event.
Tabs en navigatie: een concreet patroon
De tab-component uit Hyvä UI combineert meerdere Alpine-patronen:
<div x-data="{ active: 'beschrijving' }">
<!-- Tabs nav -->
<div class="flex border-b border-gray-200">
<button
@click="active = 'beschrijving'"
:class="active === 'beschrijving' ? 'border-b-2 border-blue-600 text-blue-600' : 'text-gray-500'"
class="px-4 py-2 text-sm font-medium"
>
Beschrijving
</button>
<button
@click="active = 'specs'"
:class="active === 'specs' ? 'border-b-2 border-blue-600 text-blue-600' : 'text-gray-500'"
class="px-4 py-2 text-sm font-medium"
>
Specificaties
</button>
</div>
<!-- Tab content -->
<div x-show="active === 'beschrijving'" class="py-4">
<p>Productbeschrijving hier.</p>
</div>
<div x-show="active === 'specs'" class="py-4">
<p>Specificaties hier.</p>
</div>
</div>
Eén x-data op de container. Knoppen lezen en schrijven active. Content reageert via x-show. Geen externe state manager, geen component-lifecycle.
Dit is ook precies de reden dat Hyvä UI framework-agnostisch is. Dezelfde markup werkt in Magento 2 Hyvä-templates, Laravel Blade-bestanden, headless setups of statische HTML. Geen aanpassingen nodig.
Meer over het bouwen van eigen componenten bovenop deze patronen: Hyvä UI custom componenten bouwen.
Performance: waarom dit relevant is
Hyvä UI is gebouwd op de performance-filosofie van Hyvä zelf: ~95% minder JavaScript dan Luma. Alpine.js weegt gzipped minder dan 15 kB. Tailwind CSS genereert alleen gebruikte classes (tree-shaken in productie).
Het resultaat: Lighthouse-scores van 95+ zijn haalbaar zonder heroïsche optimalisaties. Wij zien dat bij Magento 2-shops die van Luma naar Hyvä migreren de Time to Interactive met 60-70% daalt.
Dat heeft direct impact op conversie. Google's eigen data: elke seconde laadtijdverbetering verhoogt conversie met 2-4%.
Wanneer Hyvä UI niet de juiste keuze is
Eerlijk zijn: er zijn gevallen waar Hyvä UI niet de beste keuze is.
- React of Vue frontends: als je een headless setup hebt met een JavaScript-framework, gebruik dan de component-library van dat framework. Alpine.js naast React mengen is geen goed idee.
- Zeer complexe frontend-state: een uitgebreide checkout met meerdere stappen, real-time validatie en afhankelijkheden tussen velden vraagt op een bepaald punt om een echte state-manager. Alpine.js heeft
Alpine.store()voor globale state, maar is geen vervanging voor Pinia of Redux in zware applicaties. - Bestaande Luma-thema's bijwerken: Hyvä UI is HTML-first en Tailwind-based. Een bestaand Luma-thema heeft andere CSS-methodologie en JavaScript-architectuur. De componenten plakken in Luma lost meer problemen dan het creëert, maar vraagt extra integratie-werk.
- Teams zonder Alpine.js-kennis: de leercurve is laag, maar bestaat. Een team dat puur in React denkt heeft een dag inwerkperiode nodig.
Voor de gevallen waar Hyvä UI wel past, is het de snelste route van design naar productie die wij kennen in de Magento 2 + Hyvä-stack.
Figma en code: één bron van waarheid
Hyvä UI bevat een Figma Community-bestand met design tokens en componenten die 1-op-1 matchen met de code. Dat is zeldzaam.
In de praktijk betekent dit: als een designer een button aanpast in Figma, gebruikt hij dezelfde tokens als de developer in Tailwind. Geen vertaalslag, geen interpretatieproblemen.
Voor teams met een designer en een developer is dit direct tijdwinst. Wij zien in projecten dat dit de feedback-loop tussen design en implementatie met 40-50% verkort.
Hyvä UI vs een Hyvä theme: het onderscheid
Dit blijft een bron van verwarring. Twee duidelijke definities:
Hyvä theme: de volledige Magento 2 frontend die Luma (RequireJS, jQuery, Knockout.js) vervangt. Dit is de basis van een moderne Magento 2-shop. Aparte licentie, aparte setup. Hyvä UI: een losse componentbibliotheek. Buttons, forms, modals, dropdowns, accordions, navigatie, tabs, product cards. Bruikbaar bovenop een Hyvä theme, maar ook in Laravel, headless of statische sites. Eenmalig ongeveer 250 euro, per-ontwikkelaar/projectlicentie, geen abonnement.Je hebt geen Hyvä theme nodig om Hyvä UI te gebruiken. En een Hyvä theme zonder Hyvä UI is ook prima, maar je begint dan de componenten zelf te schrijven.
Meer over de volledige Hyvä-stack: Hyvä UI overzicht.
Conclusie
Alpine.js patronen in Hyvä UI zijn geen magie. Het zijn vier attributen — x-data, x-show, @event, :class — consequent toegepast op HTML-first componenten. Geen build-step, geen bundler, geen framework-overhead.
Voor Magento 2 Hyvä-projecten is dit de standaard geworden bij ons. Voor Laravel en headless projecten is het een serieuze optie die we vaker inzetten dan verwacht.
Wil je weten of Hyvä UI past bij jouw specifieke project? Wij kijken graag mee. Neem contact op via /contact voor een gratis Tech Check — geen verkooppraatje, gewoon een eerlijk technisch gesprek.

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