Alpine.js Patterns
Combine server-rendered Luat templates with Alpine.js for lightweight client-side interactivity.
Why Luat + Alpine? Luat handles server-side rendering with full Lua power, while Alpine.js adds reactive behavior without a build step. Together they create fast, interactive pages with minimal JavaScript.
Setup
CDN
Add Alpine.js to your app.html before the closing </body> tag:
<script defer src="https://cdn.jsdelivr.net/npm/alpinejs@3.x.x/dist/cdn.min.js"></script>
npm
npm install alpinejs
// assets/js/app.ts
import Alpine from 'alpinejs';
window.Alpine = Alpine;
Alpine.start();
Using with HTMX
When using Alpine.js together with HTMX, Alpine components in swapped content need to be reinitialized. Alpine uses a MutationObserver that automatically detects DOM changes, but for reliable initialization during HTMX swaps, use the deferred mutations pattern:
// assets/js/app.ts
import Alpine from 'alpinejs';
import htmx from 'htmx.org';
// Pause Alpine during HTMX swaps, resume after DOM settles
document.addEventListener('htmx:beforeSwap', () => {
Alpine.deferMutations();
});
document.addEventListener('htmx:afterSettle', () => {
Alpine.flushAndStopDeferringMutations();
});
window.Alpine = Alpine;
window.htmx = htmx;
Alpine.start();
This pauses Alpine's reactivity before HTMX modifies the DOM and resumes it after the swap completes, ensuring newly injected Alpine components initialize correctly.
Use htmx:afterSettle rather than htmx:afterSwap as it fires after the DOM has fully stabilized.
Preventing Flash of Unstyled Content
Add this CSS to your stylesheet to hide elements until Alpine initializes:
[x-cloak] { display: none !important; }
Elements with x-cloak will be hidden until Alpine removes the attribute on initialization, preventing flickering of unprocessed templates.
Escaping Curly Braces
Alpine.js uses { } for JavaScript expressions. Since Luat also uses curly braces for template expressions, you must escape Alpine's braces:
<!-- Luat template -->
<div x-data="\{ count: 0 \}">
This outputs literal braces that Alpine can interpret:
<!-- Rendered HTML -->
<div x-data="{ count: 0 }">
Basic Patterns
Toggle Visibility
Use x-show to toggle element visibility and x-cloak to prevent flash:
Counter with Animation
Luat + Alpine Components
Accordion with Lua Data
Define FAQ items in a Lua script and iterate with {#each}. Alpine handles the open/close state client-side:
Tabs with Dynamic Content
Define tabs in Lua, render with {#each}, and let Alpine manage the active state:
Dropdown Menu with Lua Items
Reusable Component Patterns
Modal Component
Create a reusable Modal component that accepts a title and renders children as content:
Accordion Component
A reusable accordion item component used with Lua data: