Ziggy: Using Laravel Named Routes in JavaScript
Use your Laravel routes in JavaScript.
At a glance
- What is it?
- Ziggy exposes Laravel's named routes to front-end code through a JavaScript route() helper. It is a small package with a clear mechanism, a few sharp edges around what it publishes into the page, and a maintenance cadence you can read off the release history.
- Who is it for?
- Adopt Ziggy if your front end is already served from a Laravel Blade layout and you want one route() helper on both sides of the request. Do not adopt it if your JavaScript bundle is built and deployed separately and you are unwilling to generate a config file as part of that build, or if the full route list cannot be exposed in page HTML.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 10 days ago.
- What is it written in?
- Mainly JavaScript, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on September 25, 2026, and from our analysis. They are not legal advice.
Editorial analysis
The problem Ziggy solves for Laravel front ends
Laravel names routes in PHP. A route defined as posts.show with the URI posts/{post} is addressable from PHP as route('posts.show', $post). JavaScript has no access to that registry, so front-end code that needs the same URL either hardcodes the string or reimplements the pattern. Hardcoded strings break silently when a route URI changes, and they duplicate knowledge that already exists in the route file.
Ziggy's answer is to publish the route table into the page and ship a JavaScript function that reads it. The README describes the result plainly: Ziggy provides a JavaScript route() function that works like Laravel's. The target audience is a Laravel team whose views or single-page front end call endpoints by name, and who want the URL construction to stay in one place. It is not a router for the browser, and it does not generate controllers or clients. It is a lookup table plus a resolver.
How the route table reaches the browser
The mechanism has two halves. On the PHP side, the @routes Blade directive renders a configuration object into the HTML. The README notes that by default this output includes a list of all your application's routes and their parameters, and that this list is included in the HTML of the page and can be viewed by end users. That is the central design decision in the package, and the documentation is direct about it rather than burying it.
On the JavaScript side, route() resolves a name against that configuration. Parameters can be passed positionally, as an array, or as a keyed object; the README shows all three producing the same URL for a route with a single {post} segment. Arguments that do not match a named parameter become query parameters, and a parameter that collides with a route parameter name must be nested under the special _query key. Booleans in the query string are encoded as integers, matching Laravel's own behaviour, so draft: false becomes draft=0.
The package also reads state from the current window. Calling route() with no arguments returns a Router instance with current(), has() and a params property. The README is explicit that values from route().params are always strings, which matters when you compare them against numbers. Route-model binding and custom route key names are supported, and Laravel's URL::defaults() values are honoured, so a locale default set in middleware shows up in the generated URL.
Installing Ziggy and generating a first URL
Installation is a Composer require, since the package ships a service provider and the Blade directive from the PHP side. The JavaScript package ziggy-js is published on npm, but the README's installation path for a standard Laravel app starts with Composer.
composer require tightenco/ziggyNext, place the @routes directive in your main layout, before your application's JavaScript. The README states that once this is done the route() helper function will be available globally. The ordering is not cosmetic: the directive is what writes the route configuration into the page, so a script tag that runs first will not find it.
@routes
<script src="{{ mix('js/app.js') }}"></script>With a named route in place, the JavaScript helper takes the same name and parameters as the PHP helper. The README's own example defines a route with a {post} segment and then resolves it three ways.
route('posts.show', 1); // 'https://ziggy.test/posts/1'
route('posts.show', [1]); // 'https://ziggy.test/posts/1'
route('posts.show', { post: 1 }); // 'https://ziggy.test/posts/1'Extra keys become query parameters, so route('venues.events.show', { venue: 1, event: 2, page: 5 }) appends ?page=5. If you need a query key with the same name as a route segment, nest it under _query. For a front end built outside the Blade layout, the README points to generating and importing Ziggy's configuration rather than relying on the directive.
The route list is public by default
The most consequential limitation is stated in the installation section: the default @routes output includes every route and its parameters, and end users can read it. For an internal tool this is usually fine. For an application where the existence of an admin route, an internal endpoint or a parameter name is itself sensitive, it is not, and the default is the wrong default for that situation.
The README's remedy is the filtering section, which covers including and excluding routes and filtering with groups. That is a real mechanism, not a disclaimer, but it puts the burden on you: the safe state requires configuration, and the unconfigured state is the permissive one. Teams that add Ziggy and never revisit the directive are running with the full table exposed. That is a trade-off worth naming before adoption rather than after.
A second constraint is ordering and scope. The directive must appear before the JavaScript that calls route(), and the helper is only global in the sense that the configuration it depends on is present in the document. A script that runs in a context without that configuration has nothing to resolve against.
Ziggy compared with passing URLs through Inertia or props
The obvious alternative in a Laravel application is to stop resolving URLs in JavaScript at all. With Inertia, the server returns a component and its props, and any URL the page needs travels as a prop: the controller calls route('posts.show', $post) in PHP and hands the resulting string to the view. Nothing about the route table is published, and the front end never constructs a URL.
The difference is where the knowledge lives. Ziggy centralises route names in JavaScript and lets the front end build URLs on demand, which is convenient for client-side navigation, conditional links and code that assembles a URL from data it already has. The props approach keeps every URL server-authored and pushes a new prop whenever the page needs a new link. Ziggy is the better fit when the front end makes many routing decisions; props are the better fit when it makes few and you would rather not expose the table. Neither is a superset of the other, and a codebase can reasonably use both.
Maintenance, versioning and the MIT licence
The repository is not archived, and the last push was on 2026-09-21. Recent releases are v2.6.4 on 2026-08-18, v2.6.3 on 2026-06-23 and v2.6.2 on 2026-03-05. The gap between v2.6.2 and v2.6.3 is roughly three months, and the gap between v2.6.3 and v2.6.4 is under two months, so patch releases arrive at an irregular but continuing cadence. There is an UPGRADING.md at the repository root, which is where breaking changes between major versions are documented; for a 2.x to 2.x patch upgrade the practical work is running composer update and checking the changelog.
The npm package declares MIT, and the repository ships a LICENSE file. MIT is permissive: it allows commercial use and modification with the copyright notice retained. That is the extent of what can be said here without turning into legal advice; if your organisation has rules about which licences may enter a product, the identifier to check is MIT. One packaging detail worth knowing: ziggy-js is published as an ES module with an exports map pointing at dist/index.js and a types entry at src/js/index.d.ts, so TypeScript consumers get types from the package itself rather than from a separate @types package.
Editorial conclusion
Adopt Ziggy if your front end is already served from a Laravel Blade layout and you want one route() helper on both sides of the request. Do not adopt it if your JavaScript bundle is built and deployed separately and you are unwilling to generate a config file as part of that build, or if the full route list cannot be exposed in page HTML. Before rolling it out, verify which routes the @routes directive emits on each page, check that @routes appears before your application's JavaScript, and confirm the installed version against the release history.
Frequently asked questions
How do I install Ziggy in a Laravel app?
Install the PHP side with composer require tightenco/ziggy, then add the @routes Blade directive to your main layout before your application's JavaScript. The README states that after this the route() helper is available globally.
How do I use Ziggy in Laravel?
Define a named route in Laravel and call route() in JavaScript with that name and its parameters. Parameters can be passed as a single value, an array or a keyed object, and unmatched arguments become query parameters.
How do I set up Ziggy for a front end that is not in the Blade layout?
The README covers generating and importing Ziggy's configuration for SPAs or separate repositories as an alternative to relying on the @routes directive. It also documents importing the route() function directly.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/tighten-ziggy)