Skip to content

Alpine

The Alpine adapter provides directives for wiring custom events to Alpine handlers and preserving element state during Livewire morphs. A companion Laravel package adds Blade directives for easy asset loading.

Terminal window
npm install @voidable/ui @voidable/theme @voidable/ui-alpine

For Laravel projects, also install the Composer package:

Terminal window
composer require voidable/laravel

For standalone Alpine projects, import and start Alpine yourself:

import Alpine from 'alpinejs';
import { VoidableAlpine } from '@voidable/ui-alpine';
Alpine.plugin(VoidableAlpine);
Alpine.start();

Livewire 4 bundles Alpine and starts it automatically — do not import or start Alpine yourself. Instead, register the plugin via the alpine:init event and call registerLivewireHooks to register Livewire morph lifecycle hooks that snapshot and restore void-* element internal state during re-renders:

import { VoidableAlpine, registerLivewireHooks } from '@voidable/ui-alpine';
import '@voidable/ui';
import '@voidable/theme';
document.addEventListener('alpine:init', () => {
window.Alpine.plugin(VoidableAlpine);
});
registerLivewireHooks();

The plugin registers two directives:

  • x-void-on — Wires Voidable custom events to Alpine handlers
  • x-void-preserve — Sets wire:ignore.self on the element, preventing Livewire from morphing it

Use x-void-on:{event} to handle custom events within x-data scopes:

<div x-data="{ count: 0 }">
<void-button
variant="filled"
x-void-on:void-click="count++"
>
Clicked <span x-text="count"></span> times
</void-button>
<void-input
placeholder="Search"
x-void-on:void-change="$dispatch('filter', $event.detail)"
></void-input>
</div>

Add x-void-preserve to set wire:ignore.self on an element, preventing Livewire from morphing it during re-renders. Use this when you want to opt a specific element out of morphing entirely:

<void-dialog x-void-preserve>
<p>This dialog's open state survives Livewire updates.</p>
</void-dialog>

The voidable/laravel package provides Blade directives for loading assets without a build step:

<head>
@voidableStyles
@voidableScripts
</head>

@voidableScripts outputs a <script type="module"> tag that imports the component definitions and registers the Alpine plugin. @voidableStyles loads the theme CSS.

When using Vite to bundle your assets (the standard Laravel + Livewire setup), these directives are unnecessary. Import everything in your app.js entry point and load it with the @vite directive instead:

<head>
@vite(['resources/css/app.css', 'resources/js/app.js'])
</head>