Top Related Projects
Easily build Eloquent queries from API requests
Powerful data objects for Laravel
IDE Helper for Laravel
⚗️ Adds code analysis to Laravel improving developer productivity and code quality.
Laravel Pint is an opinionated PHP code style fixer for minimalists.
Quick Overview
Ziggy is a JavaScript package that provides a seamless way to use your Laravel named routes in JavaScript. It allows you to generate URLs and perform route model binding in your frontend code, maintaining consistency between your backend and frontend routing.
Pros
- Simplifies frontend routing by using Laravel's named routes
- Reduces duplication of route definitions between backend and frontend
- Supports route model binding, making it easy to work with resource routes
- Automatically keeps frontend routes in sync with backend changes
Cons
- Requires additional setup and configuration in Laravel projects
- Adds a small amount of overhead to your JavaScript bundle
- Limited to Laravel applications, not usable with other backend frameworks
- May require updates when upgrading Laravel versions
Code Examples
- Generate a URL for a named route:
const url = route('users.show', { user: 1 });
// Output: /users/1
- Check if the current page matches a route:
if (route().current('users.*')) {
// We're on a user page
}
- Access route parameters:
const userId = route().params.user;
- Generate a URL with query parameters:
const url = route('search', { query: 'example' }, { page: 2 });
// Output: /search?query=example&page=2
Getting Started
- Install Ziggy in your Laravel project:
composer require tightenco/ziggy
- Add Ziggy to your Blade layout:
@routes
<script src="{{ asset('js/app.js') }}"></script>
- Use Ziggy in your JavaScript:
import { route } from 'ziggy-js';
const url = route('users.show', { user: 1 });
Competitor Comparisons
Easily build Eloquent queries from API requests
Pros of Laravel Query Builder
- Offers more advanced filtering, sorting, and including relations capabilities
- Provides a fluent API for building complex queries
- Allows for easy customization and extension of query parameters
Cons of Laravel Query Builder
- Requires more setup and configuration compared to Ziggy's simplicity
- May have a steeper learning curve for developers new to query building concepts
- Focuses solely on backend query construction, while Ziggy bridges frontend-backend communication
Code Comparison
Laravel Query Builder:
$users = QueryBuilder::for(User::class)
->allowedFilters(['name', 'email'])
->allowedSorts(['name', 'created_at'])
->allowedIncludes(['posts'])
->paginate();
Ziggy:
let route = Ziggy.route('users.index', {
filter: { name: 'John', email: 'john@example.com' },
sort: '-created_at',
include: 'posts'
});
While Laravel Query Builder focuses on server-side query construction, Ziggy primarily handles route generation and frontend-backend communication. Laravel Query Builder offers more advanced querying capabilities, but Ziggy simplifies route handling across the stack. The choice between them depends on specific project requirements and the desired level of query complexity.
Powerful data objects for Laravel
Pros of Laravel-data
- Offers a more comprehensive data handling solution, including data objects, casts, and validation
- Provides built-in support for API resources and responses
- Allows for easy transformation of data between different formats (arrays, JSON, etc.)
Cons of Laravel-data
- Has a steeper learning curve due to its more complex features and concepts
- May introduce additional overhead for simpler projects that don't require extensive data handling
Code Comparison
Laravel-data:
class UserData extends Data
{
public function __construct(
public string $name,
public string $email,
#[WithCast(DateTimeInterfaceCast::class)]
public ?DateTime $birth_date
) {}
}
Ziggy:
Route::get('/users/{id}', function (string $id) {
// Route handling logic
})->name('users.show');
Laravel-data focuses on defining structured data objects with built-in casting and validation, while Ziggy primarily deals with route handling and JavaScript integration. The code snippets highlight their different purposes: Laravel-data for data management and Ziggy for route management.
While both packages enhance Laravel development, they serve different purposes. Laravel-data is more suited for projects requiring complex data handling and API interactions, whereas Ziggy excels in bridging the gap between backend routes and frontend JavaScript applications.
IDE Helper for Laravel
Pros of Laravel IDE Helper
- Provides comprehensive IDE autocompletion for Laravel facades, models, and more
- Generates PHPDoc annotations for better code documentation and type hinting
- Supports multiple IDEs and editors, enhancing development experience across platforms
Cons of Laravel IDE Helper
- Requires manual regeneration of helper files after code changes
- Can potentially increase project size due to generated files
- May cause conflicts with other packages or custom implementations in some cases
Code Comparison
Laravel IDE Helper:
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
/**
* @property int $id
* @property string $name
* @property \Carbon\Carbon $created_at
* @property \Carbon\Carbon $updated_at
*/
class User extends Model
{
// Model implementation
}
Ziggy:
<script>
const routes = @json(Ziggy::toArray());
</script>
<script src="{{ asset('vendor/ziggy/ziggy.js') }}"></script>
While Laravel IDE Helper focuses on improving PHP development experience with better autocompletion and type hinting, Ziggy aims to make Laravel routes available in JavaScript. They serve different purposes and can be used together in a Laravel project to enhance both backend and frontend development workflows.
⚗️ Adds code analysis to Laravel improving developer productivity and code quality.
Pros of Larastan
- Provides static analysis for Laravel applications, enhancing code quality and catching potential errors
- Integrates with PHPStan, offering a powerful and extensible static analysis tool
- Supports custom rules and extensions tailored for Laravel-specific patterns
Cons of Larastan
- Requires additional setup and configuration compared to Ziggy's simpler integration
- May have a steeper learning curve for developers new to static analysis tools
- Can potentially produce false positives or require additional annotations in complex scenarios
Code Comparison
Larastan configuration:
<?php
use NunoMaduro\Larastan\LarastanServiceProvider;
return [
'includes' => [
base_path('vendor/nunomaduro/larastan/extension.neon'),
],
// ...
];
Ziggy usage:
<script>
const route = window.route;
console.log(route('home'));
</script>
While Larastan focuses on static analysis for PHP code, Ziggy primarily deals with exposing Laravel routes to JavaScript. These tools serve different purposes in Laravel development, making a direct comparison challenging. Larastan enhances code quality through static analysis, while Ziggy bridges the gap between backend routes and frontend JavaScript.
Laravel Pint is an opinionated PHP code style fixer for minimalists.
Pros of Pint
- Focuses on PHP code style and formatting, providing a more specialized tool for Laravel projects
- Offers a zero-configuration setup, making it easier to integrate into existing projects
- Provides a standalone CLI tool, allowing for use outside of Laravel applications
Cons of Pint
- Limited to PHP code formatting, whereas Ziggy offers JavaScript route generation
- May require additional tools for frontend-related tasks in Laravel projects
- Less flexibility in terms of customization compared to Ziggy's route handling
Code Comparison
Pint (PHP formatting):
<?php
namespace App\Http\Controllers;
class UserController extends Controller
{
public function index()
{
// Code here
}
}
Ziggy (JavaScript route generation):
import { route } from 'ziggy-js';
const url = route('users.index');
console.log(url); // Outputs: "/users"
While both tools enhance Laravel development, they serve different purposes. Pint focuses on maintaining consistent PHP code style, while Ziggy bridges the gap between Laravel's backend routes and frontend JavaScript. Pint is more suitable for teams prioritizing backend code quality, whereas Ziggy is beneficial for projects with significant frontend interactions requiring route management.
Convert designs to code with AI
Introducing Visual Copilot: A new AI model to turn Figma designs to high quality code using your components.
Try Visual CopilotREADME
Ziggy â Use your Laravel routes in JavaScript
Ziggy provides a JavaScript route()
function that works like Laravel's, making it a breeze to use your named Laravel routes in JavaScript.
Installation
Install Ziggy in your Laravel app with Composer:
composer require tightenco/ziggy
Add the @routes
Blade directive to your main layout (before your application's JavaScript), and the route()
helper function will be available globally!
By default, the output of the
@routes
Blade directive includes a list of all your application's routes and their parameters. This route list is included in the HTML of the page and can be viewed by end users. To configure which routes are included in this list, or to show and hide different routes on different pages, see Filtering Routes.
Usage
route()
function
Ziggy's route()
function works like Laravel's route()
helperâyou can pass it the name of a route, and the parameters you want to pass to the route, and it will generate a URL.
Basic usage
Route::get('posts', fn (Request $request) => /* ... */)->name('posts.index');
route('posts.index'); // 'https://ziggy.test/posts'
Parameters
Route::get('posts/{post}', fn (Post $post) => /* ... */)->name('posts.show');
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'
Multiple parameters
Route::get('venues/{venue}/events/{event}', fn (Venue $venue, Event $event) => /* ... */)
->name('venues.events.show');
route('venues.events.show', [1, 2]); // 'https://ziggy.test/venues/1/events/2'
route('venues.events.show', { venue: 1, event: 2 }); // 'https://ziggy.test/venues/1/events/2'
Query parameters
Ziggy adds arguments that don't match any named route parameters as query parameters:
Route::get('venues/{venue}/events/{event}', fn (Venue $venue, Event $event) => /* ... */)
->name('venues.events.show');
route('venues.events.show', {
venue: 1,
event: 2,
page: 5,
count: 10,
});
// 'https://ziggy.test/venues/1/events/2?page=5&count=10'
If you need to pass a query parameter with the same name as a route parameter, nest it under the special _query
key:
route('venues.events.show', {
venue: 1,
event: 2,
_query: {
event: 3,
page: 5,
},
});
// 'https://ziggy.test/venues/1/events/2?event=3&page=5'
Like Laravel, Ziggy automatically encodes boolean query parameters as integers in the query string:
route('venues.events.show', {
venue: 1,
event: 2,
_query: {
draft: false,
overdue: true,
},
});
// 'https://ziggy.test/venues/1/events/2?draft=0&overdue=1'
Default parameter values
Ziggy supports default route parameter values (Laravel docs).
Route::get('{locale}/posts/{post}', fn (Post $post) => /* ... */)->name('posts.show');
// app/Http/Middleware/SetLocale.php
URL::defaults(['locale' => $request->user()->locale ?? 'de']);
route('posts.show', 1); // 'https://ziggy.test/de/posts/1'
Examples
HTTP request with axios
:
const post = { id: 1, title: 'Ziggy Stardust' };
return axios.get(route('posts.show', post)).then((response) => response.data);
Router
class
Calling Ziggy's route()
function with no arguments will return an instance of its JavaScript Router
class, which has some other useful properties and methods.
Check the current route: route().current()
// Laravel route called 'events.index' with URI '/events'
// Current window URL is https://ziggy.test/events
route().current(); // 'events.index'
route().current('events.index'); // true
route().current('events.*'); // true
route().current('events.show'); // false
route().current()
optionally accepts parameters as its second argument, and will check that their values also match in the current URL:
// Laravel route called 'venues.events.show' with URI '/venues/{venue}/events/{event}'
// Current window URL is https://myapp.com/venues/1/events/2?hosts=all
route().current('venues.events.show', { venue: 1 }); // true
route().current('venues.events.show', { venue: 1, event: 2 }); // true
route().current('venues.events.show', { hosts: 'all' }); // true
route().current('venues.events.show', { venue: 6 }); // false
Check if a route exists: route().has()
// Laravel app has only one named route, 'home'
route().has('home'); // true
route().has('orders'); // false
Retrieve the current route params: route().params
// Laravel route called 'venues.events.show' with URI '/venues/{venue}/events/{event}'
// Current window URL is https://myapp.com/venues/1/events/2?hosts=all
route().params; // { venue: '1', event: '2', hosts: 'all' }
Note: parameter values retrieved with
route().params
will always be returned as strings.
Route-model binding
Ziggy supports Laravel's route-model binding, and can even recognize custom route key names. If you pass route()
a JavaScript object as a route parameter, Ziggy will use the registered route-model binding keys for that route to find the correct parameter value inside the object. If no route-model binding keys are explicitly registered for a parameter, Ziggy will use the object's id
key.
// app/Models/Post.php
class Post extends Model
{
public function getRouteKeyName()
{
return 'slug';
}
}
Route::get('blog/{post}', function (Post $post) {
return view('posts.show', ['post' => $post]);
})->name('posts.show');
const post = {
id: 3,
title: 'Introducing Ziggy v1',
slug: 'introducing-ziggy-v1',
date: '2020-10-23T20:59:24.359278Z',
};
// Ziggy knows that this route uses the 'slug' route-model binding key:
route('posts.show', post); // 'https://ziggy.test/blog/introducing-ziggy-v1'
Ziggy also supports custom keys for scoped bindings declared directly in a route definition:
Route::get('authors/{author}/photos/{photo:uuid}', fn (Author $author, Photo $photo) => /* ... */)
->name('authors.photos.show');
const photo = {
uuid: '714b19e8-ac5e-4dab-99ba-34dc6fdd24a5',
filename: 'sunset.jpg',
}
route('authors.photos.show', [{ id: 1, name: 'Ansel' }, photo]);
// 'https://ziggy.test/authors/1/photos/714b19e8-ac5e-4dab-99ba-34dc6fdd24a5'
TypeScript
Ziggy includes TypeScript type definitions, and an Artisan command that can generate additional type definitions to enable route name and parameter autocompletion.
To generate route types, run the ziggy:generate
command with the --types
or --types-only
option:
php artisan ziggy:generate --types
To make your IDE aware that Ziggy's route()
helper is available globally, and to type it correctly, add a declaration like this in a .d.ts
file somewhere in your project:
import { route as routeFn } from 'ziggy-js';
declare global {
var route: typeof routeFn;
}
If you don't have Ziggy's NPM package installed, add the following to your jsconfig.json
or tsconfig.json
to load Ziggy's types from your vendor directory:
{
"compilerOptions": {
"paths": {
"ziggy-js": ["./vendor/tightenco/ziggy"]
}
}
}
JavaScript frameworks
[!NOTE] Many applications don't need the additional setup described hereâthe
@routes
Blade directive makes Ziggy'sroute()
function and config available globally, including within bundled JavaScript files.
If you are not using the @routes
Blade directive, you can import Ziggy's route()
function and configuration directly into JavaScript/TypeScript files.
Generating and importing Ziggy's configuration
Ziggy provides an Artisan command to output its config and routes to a file:
php artisan ziggy:generate
This command places your configuration in resources/js/ziggy.js
by default, but you can customize this path by passing an argument to the Artisan command or setting the ziggy.output.path
config value.
The file ziggy:generate
creates looks something like this:
// resources/js/ziggy.js
const Ziggy = {
url: 'https://ziggy.test',
port: null,
routes: {
home: {
uri: '/',
methods: [ 'GET', 'HEAD'],
domain: null,
},
login: {
uri: 'login',
methods: ['GET', 'HEAD'],
domain: null,
},
},
};
export { Ziggy };
Importing the route()
function
You can import Ziggy like any other JavaScript library. Without the @routes
Blade directive Ziggy's config is not available globally, so it must be passed to the route()
function manually:
import { route } from '../../vendor/tightenco/ziggy';
import { Ziggy } from './ziggy.js';
route('home', undefined, undefined, Ziggy);
To simplify importing the route()
function, you can create an alias to the vendor path:
// vite.config.js
export default defineConfig({
resolve: {
alias: {
'ziggy-js': path.resolve('vendor/tightenco/ziggy'),
},
},
});
Now your imports can be shortened to:
import { route } from 'ziggy-js';
Vue
Ziggy includes a Vue plugin to make it easy to use the route()
helper throughout your Vue app:
import { createApp } from 'vue';
import { ZiggyVue } from 'ziggy-js';
import App from './App.vue';
createApp(App).use(ZiggyVue);
Now you can use the route()
function anywhere in your Vue components and templates:
<a class="nav-link" :href="route('home')">Home</a>
If you are not using the @routes
Blade directive, import Ziggy's configuration too and pass it to .use()
:
import { createApp } from 'vue';
import { ZiggyVue } from 'ziggy-js';
import { Ziggy } from './ziggy.js';
import App from './App.vue';
createApp(App).use(ZiggyVue, Ziggy);
If you're using TypeScript, you may need to add the following declaration to a .d.ts
file in your project to avoid type errors when using the route()
function in your Vue component templates:
declare module 'vue' {
interface ComponentCustomProperties {
route: typeof routeFn;
}
}
React
Ziggy includes a useRoute()
hook to make it easy to use the route()
helper in your React app:
import React from 'react';
import { useRoute } from 'ziggy-js';
export default function PostsLink() {
const route = useRoute();
return <a href={route('posts.index')}>Posts</a>;
}
If you are not using the @routes
Blade directive, import Ziggy's configuration too and pass it to useRoute()
:
import React from 'react';
import { useRoute } from 'ziggy-js';
import { Ziggy } from './ziggy.js';
export default function PostsLink() {
const route = useRoute(Ziggy);
return <a href={route('posts.index')}>Posts</a>;
}
You can also make the Ziggy
config object available globally, so you can call useRoute()
without passing Ziggy's configuration to it every time:
// app.js
import { Ziggy } from './ziggy.js';
globalThis.Ziggy = Ziggy;
SPAs or separate repos
Ziggy's route()
function is available as an NPM package, for use in JavaScript projects managed separately from their Laravel backend (i.e. without Composer or a vendor
directory). You can install the NPM package with npm install ziggy-js
.
To make your routes available on the frontend for this function to use, you can either run php artisan ziggy:generate
and add the generated config file to your frontend project, or you can return Ziggy's config as JSON from an endpoint in your Laravel API (see Retrieving Ziggy's config from an API endpoint below for an example of how to set this up).
Filtering Routes
Ziggy supports filtering the list of routes it outputs, which is useful if you have certain routes that you don't want to be included and visible in your HTML source.
[!IMPORTANT] Hiding routes from Ziggy's output is not a replacement for thorough authentication and authorization. Routes that should not be accessibly publicly should be protected by authentication whether they're filtered out of Ziggy's output or not.
Including/excluding routes
To set up route filtering, create a config file in your Laravel app at config/ziggy.php
and add either an only
or except
key containing an array of route name patterns.
Note: You have to choose one or the other. Setting both
only
andexcept
will disable filtering altogether and return all named routes.
// config/ziggy.php
return [
'only' => ['home', 'posts.index', 'posts.show'],
];
You can use asterisks as wildcards in route filters. In the example below, admin.*
will exclude routes named admin.login
, admin.register
, etc.:
// config/ziggy.php
return [
'except' => ['_debugbar.*', 'horizon.*', 'admin.*'],
];
Filtering with groups
You can also define groups of routes that you want make available in different places in your app, using a groups
key in your config file:
// config/ziggy.php
return [
'groups' => [
'admin' => ['admin.*', 'users.*'],
'author' => ['posts.*'],
],
];
Then, you can expose a specific group by passing the group name into the @routes
Blade directive:
{{-- authors.blade.php --}}
@routes('author')
To expose multiple groups you can pass an array of group names:
{{-- admin.blade.php --}}
@routes(['admin', 'author'])
Note: Passing group names to the
@routes
directive will always take precedence over your otheronly
orexcept
settings.
Other
TLS/SSL termination and trusted proxies
If your application is using TLS/SSL termination or is behind a load balancer or proxy, or if it's hosted on a service that is, Ziggy may generate URLs with a scheme of http
instead of https
, even if your app URL uses https
. To fix this, set up your Laravel app's trusted proxies according to the documentation on Configuring Trusted Proxies.
Using @routes
with a Content Security Policy
A Content Security Policy (CSP) may block inline scripts, including those output by Ziggy's @routes
Blade directive. If you have a CSP and are using a nonce to flag safe inline scripts, you can pass the nonce to the @routes
directive and it will be added to Ziggy's script tag:
@routes(nonce: 'your-nonce-here')
Disabling the route()
helper
If you only want to use the @routes
directive to make Ziggy's configuration available in JavaScript, but don't need the route()
helper function, set the ziggy.skip-route-function
config to true
.
Retrieving Ziggy's config from an API endpoint
If you need to retrieve Ziggy's config from your Laravel backend over the network, you can create a route that looks something like this:
// routes/api.php
use Tighten\Ziggy\Ziggy;
Route::get('ziggy', fn () => response()->json(new Ziggy));
Re-generating the routes file when your app routes change
If you are generating your Ziggy config as a file by running php artisan ziggy:generate
, you may want to re-run that command when your app's route files change. The example below is a Laravel Mix plugin, but similar functionality could be achieved without Mix. Huge thanks to Nuno Rodrigues for the idea and a sample implementation. See #655 for a Vite example.
Laravel Mix plugin example
const mix = require('laravel-mix');
const { exec } = require('child_process');
mix.extend('ziggy', new class {
register(config = {}) {
this.watch = config.watch ?? ['routes/**/*.php'];
this.path = config.path ?? '';
this.enabled = config.enabled ?? !Mix.inProduction();
}
boot() {
if (!this.enabled) return;
const command = () => exec(
`php artisan ziggy:generate ${this.path}`,
(error, stdout, stderr) => console.log(stdout)
);
command();
if (Mix.isWatching() && this.watch) {
((require('chokidar')).watch(this.watch))
.on('change', (path) => {
console.log(`${path} changed...`);
command();
});
};
}
}());
mix.js('resources/js/app.js', 'public/js')
.postCss('resources/css/app.css', 'public/css', [])
.ziggy();
Contributing
To get started contributing to Ziggy, check out the contribution guide.
Credits
Thanks to Caleb Porzio, Adam Wathan, and Jeffrey Way for help solidifying the idea.
Security
Please review our security policy on how to report security vulnerabilities.
License
Ziggy is open-source software released under the MIT license. See LICENSE for more information.
Top Related Projects
Easily build Eloquent queries from API requests
Powerful data objects for Laravel
IDE Helper for Laravel
⚗️ Adds code analysis to Laravel improving developer productivity and code quality.
Laravel Pint is an opinionated PHP code style fixer for minimalists.
Convert designs to code with AI
Introducing Visual Copilot: A new AI model to turn Figma designs to high quality code using your components.
Try Visual Copilot