lord/laroute
LaRoute exposes your Laravel named routes to JavaScript, letting you generate URLs client-side with a route() helper that mirrors Laravel's API. It supports route parameters, filtering, and easy integration so frontend code stays in sync with backend routes.
Laroute takes your named Laravel routes and compiles them into a plain-JS file so the browser can build URLs with the same names the backend uses.
Install and wire up (4 steps):
composer require lord/laroute
// config/app.php — only needed on Laravel < 5.5; newer versions auto-discover
Lord\Laroute\LarouteServiceProvider::class,
# 1. publish the config so you can control output location / filtering
php artisan vendor:publish --provider="Lord\Laroute\LarouteServiceProvider"
# 2. generate the JS file (defaults to public/js/laroute.js)
php artisan laroute:generate
{{-- 3. load it before your app JS --}}
<script src="{{ asset('js/laroute.js') }}"></script>
<script>
// 4. generate a URL by route name, params keyed by placeholder name
fetch(laroute.route('users.show', { user: 42 }))
</script>
Where to look first, in this order:
public/js/laroute.js — open it. It's a small IIFE containing a route map (name → URI pattern + domain). Everything the frontend can do is visible in that file.config/laroute.php — output path/filename, the JS namespace (laroute by default), absolute-vs-relative URL behaviour, and route filtering.vendor/lord/laroute/src — the package is a few hundred lines; the generator class is the authoritative answer for any "why did this route come out like that" question.First realistic use case: a Blade page with a small amount of AJAX. Instead of hardcoding /users/42/edit in JS (which breaks the moment you add a prefix or rename a segment), call laroute.route('users.edit', { user: 42 }). Same string you'd write in route() server-side.
Layout-level include, app-level usage. Load the generated file once in your main layout, then use it anywhere:
// vanilla / axios
axios.get(laroute.route('api.posts.index'));
axios.delete(laroute.route('api.posts.destroy', { post: id }));
// Vue/React components — just a global, no import needed
methods: {
url(id) { return laroute.route('orders.show', { order: id }); }
}
Named routes only. This is the pattern that matters: treat laroute as a hard argument for naming every route the frontend touches. Route::get(...)->name('reports.export') becomes a contract; anonymous routes are invisible to JS.
Extra params become a query string. Useful for the common "same page, different filters" case:
laroute.route('products.index', { page: 2, sort: 'price' })
// -> /products?page=2&sort=price
Named placeholders that exist in the URI are substituted; anything left over is appended as a query string. This means one route can cover pagination, sorting and filtering without separate endpoints.
Generate in the deploy pipeline, not by hand. The generated file is a build artifact derived from your route table:
php artisan route:cache
php artisan laroute:generate # after route cache, before/with your asset build
npm run production # Mix/Vite versions asset() so the file is cache-busted
If your frontend build hashes or versions assets, make sure the laroute file goes through the same pipeline — otherwise browsers keep a stale route map after a deploy and you get 404s that only affect returning visitors.
Filter what you expose. The published config lets you restrict which routes get emitted. Expose the frontend's API surface only; there's no reason to ship admin/webhook/health-check route names to every visitor.
Server-to-client handoff. For the "I already know the URL on the server" case, prefer Blade's route() and pass the string in via a data attribute or a window.__CONFIG__ = @json([...]) blob. Use laroute.route() for URLs only known at runtime. Mixing the two is fine and is the normal arrangement.
CI guard. Because the file is generated, a renamed route can silently break JS. A cheap test: assert that every route name your JS references appears in the generated output, or snapshot the generated file and diff it on PRs so route renames surface as reviewable changes.
php artisan laroute:generate. In local dev this means every Route::...->name(...) addition needs a regenerate; add it to a file watcher or a composer script or you will lose an hour to a phantom 404. If you skip the build step in CI, the file is whatever was committed last.laroute.route('foo') on a route without ->name() fails at runtime with no compile-time signal. This is the single most common source of "it works in Blade but not in JS".{ user: user.id }, not the object. Passing an object stringifies to [object Object] and you get a 404 with a URL that looks almost right./foo/{bar?}) are the sharp edge. Test them explicitly — omitting an optional param can leave a dangling separator or an empty segment depending on the pattern. If you rely on them heavily, prefer separate named routes.APP_URL doesn't match the public host, you get cross-origin or wrong-host links. Force relative in the config where possible, or pass the third argument to laroute.route(name, params, false) for a relative URL.<script> before your bundle, or shim it — don't expect import laroute from './laroute' to give you anything useful. The global name is configurable, so set it to something namespaced if you're on a page with other globals.public/, which fails on read-only containers/Vapor-style deploys. Generate at build time and ship the artifact; never generate at request time.laroute.action(...)) are brittle. They hardcode controller class names into JS; a namespace refactor breaks the frontend with no PHP-level error. Stick to named routes.How can I help you explore Laravel packages today?