vlados/cascader
A cascading dropdown for Laravel Livewire and Alpine.js, inspired by Ant Design's Cascader. It renders a two-column desktop dropdown with search and a mobile bottom sheet, supports light and dark mode, two-level option arrays, and standard wire:model binding.
Architecture fit
vlados/cascader is a presentation-layer package: a Blade component, an Alpine.js component, and Tailwind-styled published views. It has no migrations, no persistence, no queues, no external services, and no server-side runtime. That makes it architecturally cheap to adopt and cheap to remove — it touches only the view layer of a Laravel + Livewire + Alpine + Tailwind application.
Fits: Laravel 11–13 apps using Livewire 3/4 with a Tailwind + Alpine front end, forms that need a hierarchical single-select (category → subcategory → item), and teams already comfortable publishing and compiling vendor assets.
Does not fit: Inertia/Vue/React SPAs (the component assumes Livewire and a global Alpine), Bootstrap- or CSS-module-based design systems, Livewire 2, or any form where the cascader must support multi-select, async/server-side search, or very large taxonomies.
Integration feasibility
The install is composer require plus asset publishing, but it has three independent failure points that all present identically as "the component renders nothing":
alpine:init before Alpine.start(). Livewire 3/4 bundles and starts Alpine itself, so teams that also import Alpine from 'alpinejs' and call Alpine.start() will double-instantiate and break both the cascader and Livewire reactivity.content globs (v3) or covered by @source (v4), or every class gets purged and the dropdown renders unstyled.resources/js/vendor/cascader/, i.e. unreviewed third-party JavaScript is copied into our compiled bundle.Realistic estimate: half a day for a spike by a developer who knows Livewire/Alpine, one to two sprints to harden (wrapper component, a11y pass, docs, tests).
Technical risk
composer audit in CI, and require a human review of the published JS before it merges.Key questions
wire:model, $wire.set, an event? Is it Livewire-deferred, and does it work with wire:model.live validation?Stack fit
| Layer | Requirement | Our position |
|---|---|---|
| PHP | 8.2+ (8.3+ for Laravel 13) | Verify per app |
| Laravel | 11 / 12 / 13 | Confirm minor-version support |
| Livewire | 3 or 4 | Do not co-install a second Alpine |
| Alpine | 3 | Already present via Livewire |
| Tailwind | v3 or v4 | Confirm @source path for v4 |
| Icons | FontAwesome by default, resolver-configurable | Prefer mapping to our existing icon set |
The icon resolver is the main design-system seam — plan to swap FontAwesome for our icon component rather than shipping FA purely for this dropdown.
Migration path
Do not replace an existing cascader directly. Build an internal <x-ui.cascader> wrapper that owns the public API (options, model, placeholder, change event) and delegates to the vendor Blade component. That wrapper is the swap seam: if the package stalls, we reimplement behind the same props without touching call sites.
Parity checklist before any replacement: keyboard-only operation, type-ahead search, clearable, preselected/deep-linked values, disabled nodes, required-field validation messaging, mobile behavior on iOS Safari and Android Chrome, and empty/error states. Where parity is missing, decide explicitly: accept, wrap, or block.
Compatibility and sequencing
alpine:init, add the vendor path to Tailwind, render a static three-level tree next to the incumbent control on one low-traffic internal form.composer audit, map icons to our set, wrap the component, add a11y fixes (focus trap, Escape, ARIA) if needed.wire:model round-trips, dependent-field resets on change, server-side validation errors, and preselection from the database.Keep the feature flag until step 5 completes; the whole point is that reverting is a config change, not a code change.
Maintenance
Ownership sits with the front-end/platform team, not the package author. Budget: ~1–2 days per quarter for dependency bumps and Tailwind/Alpine/Livewire compatibility checks, plus the wrapper's own tests. Policy: exact-version pin, no automatic minor upgrades, composer audit and a JS-diff review in CI. Pre-approve a fork: if the maintainer goes quiet, we vendor the repo and patch in-house rather than block on upstream.
Support
No vendor, no SLA, no support contract — a single author and zero dependents means issues are effectively ours. Define L1/L2 internally and document the known failure signatures (below) in the team runbook so on-call isn't diagnosing a purged Tailwind class at 2 a.m.
Scaling
No server-side load: the tree ships to the client and search runs in the browser. That is fine for tens to low hundreds of nodes and progressively worse beyond that — payload size, DOM count on the mobile sheet, and O(n) filtering on low-end devices. For large catalogs, plan to prune the tree server-side per request or fall back to a different control; do not assume async loading exists.
Failure modes to document
Alpine.start().vendor/vlados/cascader (or missing @source on v4).Ramp-up
Low for a Laravel/Livewire team: half a day to first render, one sprint to production-grade (wrapper, a11y, tests, docs, runbook). Write a two-page internal doc covering the three registration gotchas and the icon override; that single artifact removes most of the recurring support cost. Given the package's immaturity (0 stars, 0 dependents, low opportunity score), adopt it only as a swappable UI convenience — never let it become load-bearing for a customer-facing flow without the wrapper and feature flag in place.
How can I help you explore Laravel packages today?