Skip to content
UI Brok

Loading…

No results

Browse catalog

Livewire

Every interactive Brok component is built on Alpine.js, not Livewire. Brok offers bounded, first-class Livewire 3 support rather than a Livewire variant of every component:

  • A wire:model passthrough guarantee on the form primitives — bind it and it forwards onto the real input.
  • A small reference set of native Livewire components (modal, tabs, toast, field, confirm, search) that bridge Alpine state to $wire at runtime.
  • Documented interop patterns ($wire.entangle, wire:ignore) for wiring any other Alpine-state component to Livewire yourself.

Everything ships using the modern Livewire 3 runtime idiom — $wire.entangle(…) inside Alpine x-data, not the older @entangle Blade directive — so the same copied markup renders fine with or without Livewire and only activates inside a real Livewire component.

The wire:model passthrough guarantee

The form primitives forward wire:model (and .live / .blur) onto the actual underlying form control — the real <input>, <select> or <textarea>, never a presentational wrapper — so Livewire tracks the field, validation and error bags exactly like a native control. This is verified by an automated test that renders the real component source and asserts the attribute lands on the control. Guaranteed for:

  • input
  • textarea
  • select
  • checkbox
  • radio
  • switch

The animated checkbox and the switch route consumer class to their visible control while keeping wire:* on the hidden native input, so binding stays correct even though the visible element differs.

{{-- All of these forward onto the real form control --}}



    
    


Note

Because ui:add copies real Blade into your project, the components are yours — wiring them to Livewire means editing the copied file, exactly as you would any other Blade component you own.

The Livewire reference components

A bounded set of components that bridge Alpine state to $wire at runtime — the cases where a server bridge genuinely adds value. They guard every $wire access on window.Livewire, so they render as inert, valid markup (no console errors) with no Livewire installed, and activate only inside a real Livewire component. Each requires Livewire 3 in your app.

Component Bridges What it does
livewire-modal $wire.entangle Open state entangled to a boolean property — the server can open/close it.
livewire-tabs $wire.entangle Active tab synced to a Livewire property in both directions.
livewire-toast $wire.on Listens for a server-dispatched event and shows a transient toast.
livewire-field $errors Wraps a control and renders its Livewire validation message.
livewire-confirm $wire.call Confirms, then invokes a Livewire action with optional params.
livewire-search $wire.entangle().live Debounced query pushed to a Livewire property for server-side search.

livewire-modal

Pass wire-open the name of a boolean Livewire property. A UI trigger opens it; a server action setting the property opens or closes it too.


    
        Delete project
    

    

This action cannot be undone.

Cancel Yes, delete
{{-- public bool $confirmOpen = false; --}}

livewire-tabs

The active tab value entangles wire-model; the Alpine active binding lets you show panels with x-show (or render server-side off the property).


    
{{-- public string $activeTab = 'overview'; --}}

livewire-toast

Drop one listener near your layout root. Dispatch the event from any Livewire action to raise a toast.



// in a Livewire component
$this->dispatch('notify', message: 'Saved.', variant: 'success');

livewire-field

Wrap any control. livewire-field reads the matching message from Livewire's shared $errors bag after a wire:model round-trip and renders it (with the label, hint and ARIA wiring).


    

livewire-confirm

A destructive/primary button that opens an inline confirmation and calls a Livewire action on confirm, with a built-in busy state.

livewire-search

A debounced search input that entangles wire-model with the .live modifier, so typing drives a server-side query after the debounce.



{{-- public string $query = '';
     public function updatedQuery() { /* filter results */ } --}}

Wiring up other components — the three tools

  • wire:model on real inputs. Components that render a native form control (a hidden <input>/<select>, e.g. field, native select, checkbox, radio) can bind directly: put wire:model on that input and Livewire tracks it like any other field.
  • @entangle for Alpine state. Components whose state lives in Alpine (switch, dialog, tabs, combobox, …) bridge that state to a Livewire property with @entangle, giving you two-way reactivity in both directions.
  • wire:ignore for Alpine-owned DOM. When a component renders DOM imperatively (canvas effects, portals, third-party widgets, anything that mutates its own children), wrap it in wire:ignore so a Livewire re-render doesn't wipe Alpine's work. Use wire:ignore.self when only the element itself (not its children) must survive re-render.

Example 1 — a switch bound to a Livewire property

x-ui.switch keeps its on/off state in Alpine. Bridge that state to a Livewire $notifications property with @entangle. Toggling the switch updates the property; changing the property server-side flips the switch.

{{-- resources/views/livewire/notification-settings.blade.php --}}
{{-- The switch's Alpine state is entangled with $notifications. .live pushes every toggle to the server immediately; drop it for deferred (next-request) syncing. --}}

Notifications are .

// app/Livewire/NotificationSettings.php
class NotificationSettings extends Component
{
    public bool $notifications = false;

    public function render()
    {
        return view('livewire.notification-settings');
    }
}

Note

The copied switch Blade already declares its own x-data. If merging an outer x-data is awkward, the cleaner pattern is to edit the copied component to accept a model name and entangle internally — see "Editing the component" below — or expose the entangled value through the component's own state object.

Example 2 — a select with wire:model

The native x-ui.select renders a real <select>, so it needs no Alpine bridge at all — bind wire:model straight onto it like any Livewire form control.


    
    
    


Selected plan: {{ $plan }}

The same applies to field, checkbox, radio, textarea and any component that forwards attributes onto a real input — pass wire:model through and Livewire validation / error bags work normally.

Example 3 — a dialog opened from server state, kept safe with wire:ignore

A dialog's open/closed state is Alpine-owned. Entangle it so the server can open it (e.g. after a save), and wrap the dialog in wire:ignore.self so a Livewire re-render doesn't reset Alpine's transition/focus state mid-animation.

Delete account

This cannot be undone.

{{-- A Livewire action can set $confirmOpen = false to close it. --}} Confirm

Editing the component (the clean approach)

Because you own the copied Blade, the most robust interop is to add an optional wire:model hook inside the component. For an Alpine-state component, read the model name from attributes and entangle when present:

{{-- inside resources/views/components/ui/switch.blade.php --}}
@php($wireModel = $attributes->wire('model')->value())

With that hook in place, consumers write the natural Livewire syntax — <x-ui.switch wire:model.live="notifications" /> — and the entangle wiring is hidden inside the component you own. Keep the Alpine-only default path so the component still works in non-Livewire pages.

Warning

Not first-class support. These are interop recipes, not a shipped Livewire integration. No Brok component is tested against Livewire's lifecycle, and the @entangle / wire:ignore wiring is your responsibility once you adopt it. If your app is Livewire-first and you want zero-glue reactive components, a runtime Livewire-native package may be the lower-friction choice — see Why Brok.

Checklist

  • Real input rendered? → wire:model directly on it.
  • Alpine-held state (open/value/selected)? → bridge with @entangle.
  • Component mutates its own DOM (canvas, portal, 3rd-party widget)? → wrap in wire:ignore / wire:ignore.self.
  • Reusing across many places? → bake the entangle hook into the copied component.