Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Ziggy Laravel Package

tightenco/ziggy

Ziggy gives you a JavaScript route() function that works just like Laravel's, so you can use your named Laravel routes in JavaScript. Add the @routes Blade directive to your layout and the route() helper is available globally.

View on GitHub
Deep Wiki
Context7

Getting Started

Install the PHP side, then drop the Blade directive into your layout:

composer require tightenco/ziggy
{{-- resources/views/layouts/app.blade.php --}}
<head>
    @routes
    @vite('resources/js/app.js')
</head>

That's it for the classic Blade + bundled-JS setup. route() is now a global and accepts the same arguments as Laravel's helper:

route('posts.index');              // https://myapp.test/posts
route('posts.show', 1);            // https://myapp.test/posts/1
route('posts.show', { post: 1 });  // same

Where to look first: the README's "Usage" and "Filtering Routes" sections — filtering matters more than it looks, because @routes inlines every named route (names, URIs, methods, domains) into the HTML by default. Your first real task is almost always replacing a hardcoded /api/posts/1 in an axios call with route('posts.show', post), immediately followed by trimming what @routes emits.

If you're building a Vue/React SPA or using a separate frontend repo, skip the directive and generate a config file instead:

php artisan ziggy:generate          # resources/js/ziggy.js
php artisan ziggy:generate --types  # + TypeScript route/param types

Then alias the vendor path in Vite so imports read cleanly:

// vite.config.js
resolve: { alias: { 'ziggy-js': path.resolve('vendor/tightenco/ziggy') } }

Implementation Patterns

Blade + axios. Ziggy shines when the server owns routing and JS just consumes it. Wrap it once so URL building and 422 handling live in one place:

const post = { id: 1, slug: 'ziggy-stardust' };
axios.get(route('posts.show', post)).then(r => r.data);
axios.post(route('posts.store'), payload);

Route-model binding. Pass the whole model object — Ziggy reads the route's binding key (getRouteKeyName(), or an inline {photo:uuid}) and falls back to id. This means no .slug/.uuid juggling in JS when the model's key changes.

Navigation state. route().current() is the reason many teams adopt Ziggy at all — it keeps "active link" logic in one place instead of comparing window.location strings:

route().current();                 // 'events.index'
route().current('events.*');       // true
route().current('venues.events.show', { venue: 1 }); // param-aware, incl. query
route().has('admin.dashboard');    // gate UI on route existence
route().params;                    // { venue: '1', event: '2' } — always strings

Framework glue. Vue: app.use(ZiggyVue) and route() works in templates; inject('route') inside <script setup>. React: const route = useRoute() (pass Ziggy if not using @routes). Both are thin wrappers, so prefer them over hand-importing the function.

Per-page scoping. @routes accepts a group name defined in config/ziggy.php, so admin pages ship admin routes and public pages don't. Combine with only/except (config or Ziggy::filter() in a service provider) to cap payload size and reduce surface area.

TypeScript. Generate types, declare the global, and opt into strict names per project:

// types/ziggy.d.ts
import { route as routeFn } from 'ziggy-js';
declare global { var route: typeof routeFn; }

declare module 'ziggy-js' {
  interface TypeConfig { strictRouteNames: true }
}

Add export {}; if the file isn't picked up.

Testing. Assert URLs on the PHP side (where route() already works) rather than asserting in JS. JS tests can construct a Router with a trimmed config object — faster and no Blade render required.

Gotchas and Tips

  • Everything is public. @routes output is visible in page source. Filter aggressively; never assume route names are secret.
  • ziggy:generate is a build artifact. Re-run it on every deploy, or SPA links silently 404 after a route rename. Put it in your deploy script / post-autoload-dump.
  • Two packages, one version. The Composer package and ziggy-js on npm can drift. Pick one strategy: either alias to vendor/tightenco/ziggy (always matched) or pin both. Mixing a cached npm ziggy-js with a newer PHP config is a classic "route exists but route().has() says no" bug.
  • Ordering and duplication. Load @routes before your bundle, and exactly once. Two directives (e.g. layout + partial) re-declare Ziggy and can produce confusing behavior.
  • Query params vs route params. Unmatched keys become query strings, which is usually what you want — but to send a query key that collides with a URI segment, nest it under _query. Booleans are coerced to 0/1, matching Laravel.
  • Default parameters are middleware-dependent. URL::defaults(['locale' => ...]) only takes effect if the middleware runs on the request that renders the page. If Ziggy emits a literal {locale}, check middleware ordering, not Ziggy.
  • Absolute URLs use APP_URL. Behind a proxy or in Docker, a stale APP_URL yields wrong hosts in JS links. The third argument toggles absoluteness: route('posts.index', {}, false).
  • Route caching. Ziggy works with route:cache, but filter callbacks that reference request state or closures can break caching — keep filters deterministic or use the config file's static only/except.
  • route().params is strings. Cast before numeric comparison; route().current() does the parsing for you.
  • CSRF isn't Ziggy's job. Add a <meta name="csrf-token"> and set axios's header yourself.
  • JSON responses need special handling. @routes only renders in Blade/HTML. For a config endpoint, render the directive in a tiny view or serialize new Ziggy from PHP and hand it to route(name, params, absolute, config).
  • Debugging fast: view source for the Ziggy object, or run php artisan ziggy:generate and inspect the file — mismatches between expected and emitted routes are almost always filtering or route-cache staleness.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
jinas/dhivatar
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
cgv/localized-resource