Quickstart Guide
This guide will walk you through creating your first Ziggy-powered feature from a fresh Laravel installation to making your first route() call in JavaScript.
Prerequisites
Before starting, ensure you have:
Laravel 9.0 or higher installed
PHP 8.1 or higher
Node.js and npm (for frontend assets)
Step 1: Install Ziggy
Install Ziggy via Composer:
composer require tightenco/ziggy
Ziggy is now installed and automatically registered!
Step 2: Create Laravel Routes
Let’s create a simple blog post feature. Add these routes to routes/web.php:
use App\Http\Controllers\ PostController ;
Route :: get ( '/' , function () {
return view ( 'welcome' );
}) -> name ( 'home' );
Route :: get ( '/posts' , [ PostController :: class , 'index' ]) -> name ( 'posts.index' );
Route :: get ( '/posts/{post}' , [ PostController :: class , 'show' ]) -> name ( 'posts.show' );
Route :: post ( '/posts' , [ PostController :: class , 'store' ]) -> name ( 'posts.store' );
The .name() method is crucial—Ziggy only works with named routes .
Step 3: Add @routes to Your Layout
Open your main layout file (typically resources/views/layouts/app.blade.php or create it if it doesn’t exist):
resources/views/layouts/app.blade.php
<! DOCTYPE html >
< html lang = " {{ str_replace ('_', '-', app () -> getLocale ()) }} " >
< head >
< meta charset = "utf-8" >
< meta name = "viewport" content = "width=device-width, initial-scale=1" >
< title > {{ config ( 'app.name' , 'Laravel' ) }} </ title >
{{-- Add Ziggy routes BEFORE your JavaScript --}}
@routes
@vite ([ 'resources/css/app.css' , 'resources/js/app.js' ])
</ head >
< body >
@yield ( 'content' )
</ body >
</ html >
Place @routes before @vite or any other JavaScript to ensure routes are available when your scripts load.
Step 4: Create a View with JavaScript
Create a posts listing page at resources/views/posts/index.blade.php:
resources/views/posts/index.blade.php
@extends ( 'layouts.app' )
@section ( 'content' )
< div class = "container" >
< h1 > Blog Posts </ h1 >
< div id = "posts-container" >
< p > Loading posts... </ p >
</ div >
< button id = "create-post-btn" class = "btn btn-primary" >
Create New Post
</ button >
</ div >
< script >
// Fetch posts using Ziggy's route() helper
fetch ( route ( 'posts.index' ))
. then ( response => response . json ())
. then ( posts => {
const container = document . getElementById ( 'posts-container' );
if ( posts . length === 0 ) {
container . innerHTML = '<p>No posts yet.</p>' ;
return ;
}
container . innerHTML = posts . map ( post => `
<div class="post">
<h2> ${ post . title } </h2>
<p> ${ post . excerpt } </p>
<a href=" ${ route ( 'posts.show' , post . id ) } " class="btn btn-link">
Read More
</a>
</div>
` ). join ( '' );
})
. catch ( error => console . error ( 'Error loading posts:' , error ));
// Handle create post button
document . getElementById ( 'create-post-btn' ). addEventListener ( 'click' , () => {
// Navigate to create page using Ziggy
window . location . href = route ( 'posts.create' );
});
</ script >
@endsection
Step 5: Use route() in Your JavaScript
Let’s create a more complex example with Axios in resources/js/app.js:
import axios from 'axios' ;
// Configure Axios defaults
axios . defaults . headers . common [ 'X-Requested-With' ] = 'XMLHttpRequest' ;
// Example 1: GET request
function loadPost ( postId ) {
return axios . get ( route ( 'posts.show' , postId ))
. then ( response => response . data )
. catch ( error => {
console . error ( 'Failed to load post:' , error );
throw error ;
});
}
// Example 2: POST request with CSRF token
function createPost ( postData ) {
return axios . post ( route ( 'posts.store' ), postData )
. then ( response => {
console . log ( 'Post created successfully!' );
// Redirect to the new post
window . location . href = route ( 'posts.show' , response . data . id );
})
. catch ( error => {
console . error ( 'Failed to create post:' , error );
throw error ;
});
}
// Example 3: Using with query parameters
function searchPosts ( searchTerm , page = 1 ) {
const url = route ( 'posts.index' , {
search: searchTerm ,
page: page ,
sort: 'created_at' ,
});
return axios . get ( url ). then ( response => response . data );
}
// Make functions available globally
window . loadPost = loadPost ;
window . createPost = createPost ;
window . searchPosts = searchPosts ;
Step 6: Test It Out
Start Your Development Server
Open Browser Console
Visit http://localhost:8000 and open your browser’s developer console (F12).
Try Ziggy Commands
Test these commands in the console: // Generate a URL
route ( 'posts.show' , 1 );
// Output: 'http://localhost:8000/posts/1'
// Check if route exists
route (). has ( 'posts.index' );
// Output: true
// Get current route
route (). current ();
// Output: 'home' (or whatever page you're on)
// List all routes
Object . keys ( Ziggy . routes );
// Output: ['home', 'posts.index', 'posts.show', 'posts.store']
Success! You’re now using Ziggy to generate type-safe URLs in JavaScript.
Common Use Cases
With Alpine.js
< div x-data = "{
posts: [],
async loadPosts() {
const response = await fetch(route('posts.index'));
this.posts = await response.json();
}
}" x-init = "loadPosts()" >
< template x-for = "post in posts" :key = "post.id" >
< a :href = "route('posts.show', post.id)" x-text = "post.title" ></ a >
</ template >
</ div >
With Vue 3
First, install the Vue plugin:
import { createApp } from 'vue' ;
import { ZiggyVue } from 'ziggy-js' ;
const app = createApp ({});
// Install Ziggy's Vue plugin
app . use ( ZiggyVue );
app . mount ( '#app' );
Then use it in your components:
< template >
< div >
< h1 > Posts </ h1 >
< div v-for = " post in posts " : key = " post . id " >
< a : href = " route ( 'posts.show' , post . id ) " >
{{ post . title }}
</ a >
</ div >
</ div >
</ template >
< script setup >
import { ref , onMounted , inject } from 'vue' ;
import axios from 'axios' ;
const route = inject ( 'route' );
const posts = ref ([]);
onMounted ( async () => {
const response = await axios . get ( route ( 'posts.index' ));
posts . value = response . data ;
});
</ script >
With React
import React , { useState , useEffect } from 'react' ;
import { useRoute } from 'ziggy-js' ;
import axios from 'axios' ;
export default function PostsList () {
const route = useRoute ();
const [ posts , setPosts ] = useState ([]);
useEffect (() => {
axios . get ( route ( 'posts.index' ))
. then ( response => setPosts ( response . data ))
. catch ( error => console . error ( error ));
}, []);
return (
< div >
< h1 > Posts </ h1 >
{ posts . map ( post => (
< div key = { post . id } >
< a href = { route ( 'posts.show' , post . id ) } >
{ post . title }
</ a >
</ div >
)) }
</ div >
);
}
< form id = "post-form" >
< input type = "text" name = "title" placeholder = "Post title" required >
< textarea name = "body" placeholder = "Post content" required ></ textarea >
< button type = "submit" > Create Post </ button >
</ form >
< script >
document . getElementById ( 'post-form' ). addEventListener ( 'submit' , async ( e ) => {
e . preventDefault ();
const formData = new FormData ( e . target );
const data = Object . fromEntries ( formData );
try {
const response = await fetch ( route ( 'posts.store' ), {
method: 'POST' ,
headers: {
'Content-Type' : 'application/json' ,
'X-CSRF-TOKEN' : document . querySelector ( 'meta[name="csrf-token"]' ). content ,
},
body: JSON . stringify ( data ),
});
const post = await response . json ();
// Redirect to the new post
window . location . href = route ( 'posts.show' , post . id );
} catch ( error ) {
console . error ( 'Error creating post:' , error );
alert ( 'Failed to create post' );
}
});
</ script >
Debugging Tips
Problem: Uncaught ReferenceError: route is not definedSolution:
Ensure @routes is added to your layout before your JavaScript
Clear your view cache: php artisan view:clear
Check that you’re on a page that uses the layout with @routes
Route 'xyz' is not in the route list
Problem: Ziggy error: route 'posts.show' is not in the route listSolution:
Verify the route exists: php artisan route:list --name=posts.show
Make sure the route has a name: ->name('posts.show')
Clear route cache: php artisan route:clear
Reload the page to get the latest route list
Problem: URLs have wrong domain or protocol (http vs https)Solution:
Next Steps
Route Parameters Learn advanced parameter handling and route model binding
Filtering Routes Control which routes are exposed to JavaScript
TypeScript Setup Enable route name autocompletion with TypeScript
Framework Integration Deep dives for Vue, React, and other frameworks
You’re All Set!
Congratulations! You’ve successfully:
Added the @routes directive
✓
Used route() in JavaScript
✓
You’re now ready to build amazing features with type-safe routing!