Ziggy.js - Laravel Routes in JavaScript
Ziggy provides a JavaScript `route()` function that seamlessly integrates with Laravel's named routes, enabling consistent URL generation in frontend applications. The current stable version is 2.6.2, and the package maintains an active release cadence with frequent patch and minor updates, reflecting ongoing development and support. Its core differentiator lies in its ability to dynamically expose Laravel's PHP-defined routes to the JavaScript environment via a Blade directive, eliminating the need to hardcode URLs or duplicate routing logic. This integration includes support for advanced Laravel features such as route-model binding, automatic handling of query parameters, and robust TypeScript definitions for a type-safe development experience. Ziggy aims to simplify the development of single-page applications (SPAs) and traditional frontend projects by ensuring that frontend routing logic remains synchronized with the backend.
Common errors
-
ReferenceError: route is not defined
cause The global `route()` function was not initialized because the `@routes` Blade directive was omitted or placed incorrectly.fixEnsure `@routes` is included in your main Blade layout file (e.g., `app.blade.php`) before any JavaScript that uses Ziggy, typically within the `<head>` or at the start of `<body>`. -
TypeError: (0, ziggy_js_1.route) is not a function
cause This error often occurs when attempting to use a CommonJS `require()` statement or an incorrect named import syntax for `ziggy-js` in an environment expecting ES Modules.fixFor ES Module environments, use `import { route } from 'ziggy-js';`. If using a bundler, ensure your configuration correctly handles ES Modules. If your project strictly uses CommonJS, consider transpilation or adjusting your build setup as `ziggy-js` is primarily designed for ESM. -
The route [your.route.name] is not found.
cause The specified route name either does not exist in your Laravel application, was misspelled, or was filtered out when Ziggy's configuration was generated.fixDouble-check the route name against your Laravel `web.php` or `api.php` files. Verify your `php artisan ziggy:generate` command or `@routes` Blade directive parameters (`except`, `only`, `group`) are not inadvertently excluding the required route.
Warnings
- gotcha By default, the `@routes` Blade directive exposes all Laravel routes and their parameters to the frontend HTML. This can be a security concern if sensitive backend routes (e.g., admin panels, internal APIs) are inadvertently exposed. Review and filter routes carefully.
- breaking In v2.6.0, the internal `qs` library for query string parsing and serialization was replaced. While typically an internal change, it *could* subtly alter query string behavior for very specific edge cases if you were relying on particular `qs` encoding/decoding characteristics.
- gotcha For Single Page Applications (SPAs) or projects with separate frontend repositories, the `Ziggy` configuration must be explicitly generated and imported, as the `@routes` Blade directive will not automatically inject it.
- breaking Support for Laravel's route model binding and interfaces was introduced in v2.6.0. Applications relying on route model binding to automatically resolve parameters based on model instances will not function correctly with Ziggy versions prior to 2.6.0.
Install
-
npm install ziggy-js -
yarn add ziggy-js -
pnpm add ziggy-js
Imports
- route
const { route } = require('ziggy-js');import { route } from 'ziggy-js'; - Router
const Router = require('ziggy-js').Router;import { Router } from 'ziggy-js'; - ZiggyConfig
import { ZiggyConfig } from 'ziggy-js';import type { ZiggyConfig } from 'ziggy-js';
Quickstart
declare global {
interface Window {
Ziggy: {
url: string;
port: null | number;
defaults: Record<string, any>;
routes: Record<string, {
uri: string;
methods: string[];
bindings?: Record<string, string>;
wheres?: Record<string, string>;
}>;
};
}
}
// Simulate Ziggy configuration typically injected by Laravel's @routes Blade directive
window.Ziggy = {
url: 'https://example.com',
port: null,
defaults: {},
routes: {
'home': { uri: '/', methods: ['GET', 'HEAD'] },
'posts.index': { uri: 'posts', methods: ['GET', 'HEAD'] },
'posts.show': { uri: 'posts/{post}', methods: ['GET', 'HEAD'], wheres: { post: '[^/]+' } },
'users.profile': { uri: 'users/{user}', methods: ['GET', 'HEAD'], bindings: { user: 'id' }, wheres: { user: '[^/]+' } },
'venues.events.show': { uri: 'venues/{venue}/events/{event}', methods: ['GET', 'HEAD'], wheres: { venue: '[^/]+', event: '[^/]+' } }
}
};
import { route } from 'ziggy-js';
// Basic usage
console.log('Home route:', route('home').toString());
// Expected: https://example.com/
// With parameters
console.log('Post show (ID 1):', route('posts.show', { post: 1 }).toString());
// Expected: https://example.com/posts/1
// Multiple parameters
console.log('Venue 1 Event 2:', route('venues.events.show', { venue: 1, event: 2 }).toString());
// Expected: https://example.com/venues/1/events/2
// Parameters with query strings
console.log('Post show with query:', route('posts.show', { post: 1, page: 2, sort: 'asc' }).toString());
// Expected: https://example.com/posts/1?page=2&sort=asc
// Route model binding (since v2.6.0)
const user = { id: 5, name: 'Alice' }; // Simulate a Laravel model object
console.log('User profile (ID 5):', route('users.profile', user).toString());
// Expected: https://example.com/users/5
// Simplified current route check (route().current() typically uses window.location)
const isCurrentRoute = (path: string, routeName: string, params: Record<string, any>) => {
const generatedPath = route(routeName, params, false).relative(); // Get relative path
return path === generatedPath; // Simple path match
};
console.log(`Is '/posts/1' the current 'posts.show' route? ${isCurrentRoute('/posts/1', 'posts.show', { post: 1 })}`);
// Expected: Is '/posts/1' the current 'posts.show' route? true