Documentation Index Fetch the complete documentation index at: https://mintlify.com/tighten/ziggy/llms.txt
Use this file to discover all available pages before exploring further.
Instead of generating a static configuration file, you can create an API endpoint that serves Ziggy’s configuration dynamically. This approach is particularly useful for SPAs or applications where the frontend is in a separate repository.
Overview
Serving Ziggy’s configuration from an API endpoint provides several benefits:
Always up-to-date : No need to regenerate static files when routes change
Dynamic filtering : Serve different routes based on authentication or user permissions
Simplified deployment : No need to copy config files between projects
Cache control : Implement custom caching strategies
Creating the Basic Endpoint
Add the API route
Create a route that returns a new Ziggy instance as JSON: // routes/api.php
use Tighten\Ziggy\ Ziggy ;
Route :: get ( 'ziggy' , fn () => response () -> json ( new Ziggy ));
This endpoint will be available at /api/ziggy.
Test the endpoint
Visit the endpoint in your browser or use curl: curl https://yourapp.com/api/ziggy
You should see a JSON response like: {
"url" : "https://yourapp.com" ,
"port" : null ,
"defaults" : {},
"routes" : {
"home" : {
"uri" : "/" ,
"methods" : [ "GET" , "HEAD" ],
"domain" : null
},
"posts.index" : {
"uri" : "posts" ,
"methods" : [ "GET" , "HEAD" ],
"domain" : null
}
}
}
Fetch from your frontend
Fetch the configuration when your application initializes: import { route } from 'ziggy-js' ;
let ziggyConfig = null ;
async function loadZiggyConfig () {
const response = await fetch ( 'https://yourapp.com/api/ziggy' );
ziggyConfig = await response . json ();
}
// Call this when your app starts
await loadZiggyConfig ();
// Now you can use the route function
const url = route ( 'posts.show' , 1 , undefined , ziggyConfig );
Advanced Configurations
Filtering Routes
Serve only specific routes to your frontend:
// routes/api.php
use Tighten\Ziggy\ Ziggy ;
Route :: get ( 'ziggy' , function () {
return response () -> json (
new Ziggy ( only : [ 'api.*' , 'posts.*' ])
);
});
Or exclude certain routes:
Route :: get ( 'ziggy' , function () {
return response () -> json (
new Ziggy ( except : [ 'admin.*' , '_debugbar.*' ])
);
});
Using Groups
Serve different route groups based on user permissions:
Route :: get ( 'ziggy' , function () {
$groups = [ 'public' ];
if ( auth () -> check ()) {
$groups [] = 'authenticated' ;
}
if ( auth () -> user () ?-> isAdmin ()) {
$groups [] = 'admin' ;
}
return response () -> json (
new Ziggy ( group : $groups )
);
});
Define groups in config/ziggy.php:
// config/ziggy.php
return [
'groups' => [
'public' => [ 'home' , 'posts.index' , 'posts.show' ],
'authenticated' => [ 'dashboard' , 'profile.*' ],
'admin' => [ 'admin.*' , 'users.*' ],
],
];
Implement caching to reduce server load:
Route :: get ( 'ziggy' , function () {
$ziggy = new Ziggy ;
return response ()
-> json ( $ziggy )
-> header ( 'Cache-Control' , 'public, max-age=3600' ) // Cache for 1 hour
-> header ( 'ETag' , md5 ( json_encode ( $ziggy )));
});
Authenticated Endpoint
Protect the endpoint with authentication:
Route :: middleware ( 'auth:sanctum' ) -> get ( 'ziggy' , function () {
return response () -> json (
new Ziggy ( only : [ 'api.*' ])
);
});
Frontend Integration
React Hook Example
Create a custom hook to fetch and manage Ziggy config:
import { useState , useEffect } from 'react' ;
import { route as ziggyRoute } from 'ziggy-js' ;
export function useZiggy () {
const [ config , setConfig ] = useState ( null );
const [ loading , setLoading ] = useState ( true );
const [ error , setError ] = useState ( null );
useEffect (() => {
fetch ( '/api/ziggy' )
. then ( res => res . json ())
. then ( data => {
setConfig ( data );
setLoading ( false );
})
. catch ( err => {
setError ( err );
setLoading ( false );
});
}, []);
const route = ( name , params ) => {
if ( ! config ) return '#' ;
return ziggyRoute ( name , params , undefined , config );
};
return { route , loading , error };
}
Use in your components:
import { useZiggy } from './hooks/useZiggy' ;
function PostsList () {
const { route , loading } = useZiggy ();
if ( loading ) return < div > Loading... </ div > ;
return (
< div >
< a href = { route ( 'posts.index' ) } > All Posts </ a >
< a href = { route ( 'posts.create' ) } > Create Post </ a >
</ div >
);
}
Vue Composable Example
// composables/useZiggy.js
import { ref , onMounted } from 'vue' ;
import { route as ziggyRoute } from 'ziggy-js' ;
const config = ref ( null );
const loading = ref ( true );
export function useZiggy () {
onMounted ( async () => {
if ( config . value ) return ; // Already loaded
try {
const response = await fetch ( '/api/ziggy' );
config . value = await response . json ();
} catch ( error ) {
console . error ( 'Failed to load Ziggy config:' , error );
} finally {
loading . value = false ;
}
});
const route = ( name , params ) => {
if ( ! config . value ) return '#' ;
return ziggyRoute ( name , params , undefined , config . value );
};
return { route , loading };
}
Global Configuration Strategy
Load the config once and make it globally available:
// app.js
import { Ziggy } from 'ziggy-js' ;
async function initializeApp () {
// Fetch Ziggy config
const response = await fetch ( '/api/ziggy' );
const config = await response . json ();
// Make it globally available
window . Ziggy = config ;
// Now initialize your app
// ...
}
initializeApp ();
Then use it anywhere without passing config:
import { route } from 'ziggy-js' ;
// Config is automatically picked up from window.Ziggy
const url = route ( 'posts.show' , 1 );
Server-Side Caching
Cache the Ziggy config to avoid regenerating it on every request:
use Illuminate\Support\Facades\ Cache ;
use Tighten\Ziggy\ Ziggy ;
Route :: get ( 'ziggy' , function () {
$config = Cache :: remember ( 'ziggy-config' , 3600 , function () {
return new Ziggy ;
});
return response () -> json ( $config );
});
Invalidate the cache when routes change:
// In your deployment script or AppServiceProvider
Cache :: forget ( 'ziggy-config' );
Client-Side Caching
Cache the config in localStorage:
async function loadZiggyConfig () {
// Try to load from cache
const cached = localStorage . getItem ( 'ziggy-config' );
const cacheTime = localStorage . getItem ( 'ziggy-config-time' );
// Use cache if less than 1 hour old
if ( cached && cacheTime && Date . now () - parseInt ( cacheTime ) < 3600000 ) {
return JSON . parse ( cached );
}
// Fetch fresh config
const response = await fetch ( '/api/ziggy' );
const config = await response . json ();
// Store in cache
localStorage . setItem ( 'ziggy-config' , JSON . stringify ( config ));
localStorage . setItem ( 'ziggy-config-time' , Date . now (). toString ());
return config ;
}
Route :: get ( 'ziggy' , function () {
$ziggy = new Ziggy ;
$etag = md5 ( json_encode ( $ziggy ));
if ( request () -> header ( 'If-None-Match' ) === $etag ) {
return response ( '' , 304 ); // Not Modified
}
return response ()
-> json ( $ziggy )
-> header ( 'ETag' , $etag )
-> header ( 'Cache-Control' , 'must-revalidate' );
});
Troubleshooting
Ensure the route is properly defined: php artisan route:list --path=ziggy
Verify the route appears in the list. If not, clear the route cache:
If your frontend is on a different domain, configure CORS: // config/cors.php
return [
'paths' => [ 'api/*' ],
'allowed_origins' => [ 'https://yourfrontend.com' ],
'allowed_methods' => [ 'GET' ],
'allowed_headers' => [ '*' ],
];
Check your route filtering configuration: // Make sure filters aren't too restrictive
return response () -> json (
( new Ziggy ) -> toArray () // Debug: see all properties
);
If using Sanctum or other authentication: const response = await fetch ( '/api/ziggy' , {
headers: {
'Authorization' : `Bearer ${ token } ` ,
'Accept' : 'application/json' ,
},
});
Security Considerations
Filter sensitive routes : Never expose admin or internal routes to public endpoints
Rate limiting : Apply rate limiting to prevent abuse
Route :: middleware ( 'throttle:60,1' ) -> get ( 'ziggy' , /* ... */ );
Authentication : Consider requiring authentication for production apps
CORS : Only allow trusted origins in production
Best Practices
Cache aggressively : Route configurations rarely change, so cache liberally
Use groups : Filter routes appropriately for different user types
Version your endpoint : Consider versioning (e.g., /api/v1/ziggy) for future changes
Monitor performance : Track endpoint response times and cache hit rates
Handle errors gracefully : Provide fallbacks if config loading fails