Skip to main content

Overview

Ziggy automatically adds arguments that don’t match any named route parameters as query parameters. This makes it easy to add filters, pagination, and other query strings to your URLs.

Automatic Query Parameters

Any parameters that don’t match route segments are automatically appended 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'
In this example:
  • venue and event are route parameters (matched to {venue} and {event})
  • page and count don’t match any route segments, so they become query parameters

The _query Key

Sometimes you need to pass a query parameter with the same name as a route parameter. Use the special _query key to handle these conflicts:
route('venues.events.show', {
  venue: 1,
  event: 2,
  _query: {
    event: 3,
    page: 5,
  },
});
// 'https://ziggy.test/venues/1/events/2?event=3&page=5'
Parameters inside _query are always treated as query parameters, even if they match route parameter names.

When to Use _query

// ❌ This won't work as expected
route('events.show', {
  event: 1,      // Route parameter
  event: 'past', // Query parameter? This overwrites the above!
});

Boolean Query Parameters

Ziggy automatically encodes boolean values as integers, matching Laravel’s behavior:
route('venues.events.show', {
  venue: 1,
  event: 2,
  _query: {
    draft: false,
    overdue: true,
  },
});
// 'https://ziggy.test/venues/1/events/2?draft=0&overdue=1'
This behavior is implemented in the encoder function (Router.js:65-66) using Ziggy’s query string serializer:
encoder: (value, encoder) =>
  typeof value === 'boolean' ? Number(value) : encoder(value)

Working with Arrays

Ziggy uses the indices array format for query parameters:
route('posts.index', {
  tags: ['javascript', 'laravel', 'ziggy'],
});
// 'https://ziggy.test/posts?tags[0]=javascript&tags[1]=laravel&tags[2]=ziggy'
Ziggy uses the qs library with these options:
  • addQueryPrefix: true - Adds the ? prefix
  • arrayFormat: 'indices' - Arrays become key[0]=value&key[1]=value
  • encodeValuesOnly: true - Only encodes values, not keys
  • skipNulls: true - Omits null/undefined values
This matches Laravel’s query string handling conventions.

Null and Undefined Values

Null and undefined parameters are automatically skipped:
route('posts.index', {
  page: 5,
  search: null,
  filter: undefined,
});
// 'https://ziggy.test/posts?page=5'
// 'search' and 'filter' are omitted

Nested Objects

You can pass nested objects as query parameters:
route('posts.index', {
  filter: {
    status: 'published',
    author: 'jane',
  },
});
// 'https://ziggy.test/posts?filter[status]=published&filter[author]=jane'

Common Use Cases

Pagination

route('posts.index', { page: 2, perPage: 15 });
// 'https://ziggy.test/posts?page=2&perPage=15'

Filtering and Sorting

route('products.index', {
  category: 'electronics',
  sort: 'price',
  order: 'asc',
  inStock: true,
});
// 'https://ziggy.test/products?category=electronics&sort=price&order=asc&inStock=1'

Search with Route Parameters

route('users.posts.index', {
  user: 1,
  search: 'ziggy',
  status: 'published',
});
// 'https://ziggy.test/users/1/posts?search=ziggy&status=published'

Tab or View State

route('dashboard.index', {
  tab: 'analytics',
  range: '7d',
});
// 'https://ziggy.test/dashboard?tab=analytics&range=7d'

Implementation Details

The toString() method in Router.js handles query parameter extraction:
const unhandled = Object.keys(this._params)
  .filter((key) => !this._route.parameterSegments.some(({ name }) => name === key))
  .filter((key) => key !== '_query')
  .reduce((result, current) => ({ ...result, [current]: this._params[current] }), {});

return (
  this._route.compile(this._params) +
  stringify(
    { ...unhandled, ...this._params['_query'] },
    { /* options */ }
  )
);
This:
  1. Identifies parameters that don’t match route segments
  2. Excludes the _query key itself
  3. Merges unmatched parameters with _query contents
  4. Serializes them using the qs library

Best Practices

Only use _query when you have naming conflicts. For most cases, automatic query parameter handling is cleaner:
// ✅ Clean and simple
route('posts.index', { page: 5, search: 'ziggy' });

// ❌ Unnecessarily verbose
route('posts.index', { _query: { page: 5, search: 'ziggy' } });
// ✅ Easy to read and debug
route('posts.index', { status: 'published', author: 'jane' });

// ⚠️ Works but more complex
route('posts.index', { filter: { status: 'published', author: 'jane' } });
Make sure your query parameters match what your Laravel controller expects:
// Laravel Controller
public function index(Request $request)
{
    $page = $request->query('page');
    $search = $request->query('search');
}
// JavaScript - matching parameter names
route('posts.index', { page: 2, search: 'laravel' });

Next Steps

Route-Model Binding

Learn about route-model binding in Ziggy

Default Parameters

Set default parameter values for your routes

Build docs developers (and LLMs) love