bastinald/laravel-livewire-routes
Laravel Livewire Routes lets you register Livewire components as routes with a simple, expressive API, similar to Laravel's resource routing. Define component routes in one place, keep URLs clean, and skip boilerplate controller wiring for faster Livewire development.
composer require bastinald/laravel-livewire-routes. The package auto-registers a service provider and a Route::livewire() macro — no publish step, no config file.app/Http/Livewire, Livewire::component() registration). Verify composer.json constraints before pulling it into a Livewire 3 app.routes/web.php for the call sites, then the ~50 lines in vendor/bastinald/laravel-livewire-routes/src. It's a macro that registers a GET route pointing at a Livewire component; reading it once tells you exactly which arguments and chaining it supports.app/Http/Livewire (L2) or an explicit Livewire::component('users', \App\Http\Livewire\Users::class).// routes/web.php
Route::livewire('/users', 'users')->name('users');
Visit /users. If you get "Component not found," your alias doesn't match a registered component — that's the #1 first-run failure.
Route::livewire('/dashboard', 'dashboard')->name('dashboard');
// behaviorally equivalent to:
Route::get('/dashboard', \App\Http\Livewire\Dashboard::class);
Route::livewire('/posts', 'posts.index')->name('posts.index');
Route::livewire('/posts/create', 'posts.create')->name('posts.create');
Route::livewire('/posts/{post}', 'posts.show')->name('posts.show');
Route::livewire('/posts/{post}/edit', 'posts.edit')->name('posts.edit');
Route, so everything chains normally:Route::middleware(['auth', 'verified'])->group(function () {
Route::livewire('/settings', 'settings')->name('settings');
});
mount() — URI parameters are passed by name; type-hint for implicit model binding:// routes/web.php
Route::livewire('/posts/{post}', 'posts.show');
// app/Http/Livewire/Posts/Show.php
public function mount(Post $post) { $this->post = $post; }
Layouts are still your job. The package renders the component but does not own the Blade layout. In L2, set protected $layout = 'layouts.app'; on the component or chain ->layout() on the rendered view; in L3 use #[Layout('layouts.app')].
Incremental adoption — mixed route styles coexist fine. Keep Blade pages on Route::get(...) and migrate one page at a time.
Testing — hit the URL like any other route, then assert Livewire mounted:
$this->get('/users')->assertOk()->assertSeeLivewire('users');
For component behavior, skip the route entirely and use Livewire::test(Users::class).
route('posts.show', $post) and Route::has() behave normally. No special handling for redirects or url()->previous().Livewire 3 has this built in. Route::get('/users', Users::class) with #[Layout] is now native. This package's value is mostly the string-alias shorthand and L2-era compatibility. If you're on L3, consider this package optional rather than load-bearing.
php artisan route:cache can break. If the macro wraps the component in a closure internally, route caching fails with "Unable to prepare route for serialization. Uses Closure." Run route:cache in CI before trusting this in production; if it fails, keep these routes off the cached set or convert them to direct class routes.
Parameter-name typos fail silently. /posts/{post} requires mount(Post $post) (or an untyped $post). Naming it $postId or $id gives you a null property with no error — the page renders in a broken state.
Alias mismatch is the classic 404-ish bug. Debug with php artisan route:list --path=posts to confirm the route exists, then check the alias actually resolves via Livewire::component(...) or auto-discovery. If auto-discovery is scoped incorrectly (custom namespace, nested folders), manual registration is the reliable fallback.
Layout-less components look broken. No layout means Livewire's bare fallback HTML — no Tailwind, no app chrome. If a page renders but looks unstyled, check the layout binding before the CSS pipeline.
Plain (non-Livewire) forms on the page still need their own routes. The macro only registers a GET page render. Livewire's update endpoint handles wire:submit forms; any traditional POST form (uploads, webhooks) needs a separate Route::post(...).
Debugging in the wild: route:list first, then Laravel Telescope or a logging middleware to confirm the request is hitting the route at all. If Livewire updates aren't firing on a full-page component, verify the root element is a single wrapper <div> (L2 requirement) before blaming the routing.
Lock-in is minimal — plan the exit. The swap back to vanilla Livewire is mechanical:
// before
Route::livewire('/users', 'users');
// after (Livewire 3)
Route::get('/users', \App\Http\Livewire\Users::class);
Wrap it in your own tiny helper (livewireRoute($uri, $component)) across the app, and you can change the implementation — including adding middleware or switching APIs — in one file when you upgrade.
How can I help you explore Laravel packages today?