> ## Documentation Index
> Fetch the complete documentation index at: https://kawax.biz/llms.txt
> Use this file to discover all available pages before exploring further.

# Introducing Laravel Wayfinder

> Learn about Laravel Wayfinder, adopted in the Inertia starter kits as the Ziggy replacement. Covers how it type-safely connects a Laravel backend and a TypeScript frontend, differences from Ziggy, installation and basic usage, and next-generation features evolving on the next branch.

## What is Wayfinder?

**Laravel Wayfinder** is a package that bridges a Laravel backend and a TypeScript frontend with zero friction. It auto-generates fully typed TypeScript functions from your controllers and routes, so you can call Laravel endpoints as functions directly from your frontend code.

Hardcoded URLs, manually managed route parameters, and hand-syncing backend changes—all of that goes away.

<Info>
  Wayfinder is in Beta (currently v0.1.x). The API may change before v1.0.0. All notable changes are recorded in the [CHANGELOG](https://github.com/laravel/wayfinder/blob/main/CHANGELOG.md).
</Info>

***

## Differences between Ziggy and Wayfinder

### What is Ziggy?

[Ziggy](https://github.com/tighten/ziggy) has long been a widely used route helper in the Laravel ecosystem. It exposes Laravel route definitions to JavaScript so you can generate URLs like `route('posts.show', { id: 1 })`.

### Why replace it with Wayfinder?

Because Ziggy handles route names and parameters as strings, its TypeScript integration is limited. Typos in route names or wrong parameter names show up only as runtime errors.

Wayfinder is TypeScript-first and generates controller methods as **importable functions**.

| Comparison        | Ziggy                            | Wayfinder                              |
| ----------------- | -------------------------------- | -------------------------------------- |
| Route reference   | `route('posts.show', { id: 1 })` | `import { show } from "@/actions/..."` |
| Type safety       | Limited types                    | Full TypeScript types                  |
| IDE support       | Weak completion                  | Full completion and type-checking      |
| Tree shaking      | All routes bundled               | Only used routes bundled               |
| Generation timing | Injected at runtime              | Statically generated at build time     |

In Inertia-based Laravel starter kits (React, Vue, Svelte), Wayfinder is adopted as the standard.

***

## Installation

### 1. Install the server-side package via Composer

```bash theme={null}
composer require laravel/wayfinder
```

### 2. Install the Vite plugin via NPM

```bash theme={null}
npm i -D @laravel/vite-plugin-wayfinder
```

### 3. Add the plugin to `vite.config.js`

```ts theme={null}
import { wayfinder } from "@laravel/vite-plugin-wayfinder";
import { defineConfig } from "vite";
import laravel from "laravel-vite-plugin";

export default defineConfig({
    plugins: [
        laravel({
            input: ["resources/js/app.ts"],
            refresh: true,
        }),
        wayfinder(),
    ],
});
```

With the Vite plugin added, TypeScript files are automatically regenerated whenever PHP files or route files change during dev.

***

## Generating TypeScript definitions

Generate TypeScript files with the `wayfinder:generate` command.

```bash theme={null}
php artisan wayfinder:generate
```

By default, three directories are generated under `resources/js`.

```
resources/js/
├── actions/         # Controller action functions
│   └── App/Http/Controllers/
│       └── PostController.ts
├── routes/          # Named-route functions
│   └── post.ts
└── wayfinder/       # Type definitions
    └── types.ts
```

<Tip>
  Because generated files are fully re-created on every build, add them to `.gitignore`. Exclude the three directories `wayfinder`, `actions`, and `routes` together.
</Tip>

To change the output location, use the `--path` option.

```bash theme={null}
php artisan wayfinder:generate --path=resources/js/api
```

You can also generate only actions or only routes.

```bash theme={null}
php artisan wayfinder:generate --skip-actions  # Generate only routes
php artisan wayfinder:generate --skip-routes   # Generate only actions
```

***

## Basic usage

### Importing and using an action

Here's an example that produces the URL for `PostController@show`.

```ts theme={null}
import { show } from "@/actions/App/Http/Controllers/PostController";

show(1);
// { url: "/posts/1", method: "get" }
```

Use `.url()` when you only need the URL.

```ts theme={null}
show.url(1); // "/posts/1"
```

You can also specify a particular HTTP method.

```ts theme={null}
show.head(1); // { url: "/posts/1", method: "head" }
```

### Passing parameters

Wayfinder functions accept parameters in several forms.

```ts theme={null}
import { update } from "@/actions/App/Http/Controllers/PostController";

// Single parameter
show(1);
show({ id: 1 });

// Multiple parameters
update([1, 2]);
update({ post: 1, author: 2 });
update({ post: { id: 1 }, author: { id: 2 } });
```

When the route uses key binding (`/posts/{post:slug}`), pass that value.

```ts theme={null}
// When the route is /posts/{post:slug}
show("my-new-post");
show({ slug: "my-new-post" });
```

### Importing a whole controller

You can also import a whole controller and call its methods.

```ts theme={null}
import PostController from "@/actions/App/Http/Controllers/PostController";

PostController.show(1);
PostController.index();
```

<Warning>
  Importing a whole controller defeats tree shaking, so every action ends up in the bundle. Importing individually keeps the final bundle smaller.
</Warning>

### Single-action controllers

For single-action (invokable) controllers, call the imported function directly.

```ts theme={null}
import StorePostController from "@/actions/App/Http/Controllers/StorePostController";

StorePostController(); // { url: "/posts", method: "post" }
```

### Importing named routes

Access by route name using files under `routes/`.

```ts theme={null}
import { show } from "@/routes/post";

// When the route name is `post.show`
show(1); // { url: "/posts/1", method: "get" }
```

### Query parameters

Every Wayfinder function accepts a `query` option to add query parameters.

```ts theme={null}
import { show } from "@/actions/App/Http/Controllers/PostController";

show(1, { query: { page: 1, sort_by: "name" } });
// { url: "/posts/1?page=1&sort_by=name", method: "get" }
```

Use `mergeQuery` to merge with the current URL's query parameters.

```ts theme={null}
// Current URL: /posts/1?page=1&sort_by=category&q=shirt

show.url(1, { mergeQuery: { page: 2, sort_by: "name" } });
// "/posts/1?page=2&sort_by=name&q=shirt"

// Set null to remove a parameter
show.url(1, { mergeQuery: { sort_by: null } });
// "/posts/1?page=1&q=shirt"
```

### Form variants

For use with traditional HTML forms, generate with `--with-form` and use the `.form` variant.

```bash theme={null}
php artisan wayfinder:generate --with-form
```

```tsx theme={null}
import { store, update } from "@/actions/App/Http/Controllers/PostController";

// React example
const Page = () => (
    <form {...store.form()}>
        {/* <form action="/posts" method="post"> */}
    </form>
);

const EditPage = () => (
    <form {...update.form(1)}>
        {/* <form action="/posts/1?_method=PATCH" method="post"> */}
    </form>
);
```

***

## Combining Inertia and Wayfinder

Combining Inertia form helpers with Wayfinder lets you submit forms without writing any URL strings.

```ts theme={null}
import { useForm } from "@inertiajs/react";
import { store } from "@/actions/App/Http/Controllers/PostController";

const form = useForm({ name: "My Post" });

form.submit(store()); // Submits to POST /posts
```

It works the same way with the `Link` component.

```tsx theme={null}
import { Link } from "@inertiajs/react";
import { show } from "@/actions/App/Http/Controllers/PostController";

const Nav = () => (
    <Link href={show(1)}>View post</Link>
);
```

***

## Adoption in starter kits

If you create a new project with `laravel new` and choose React, Vue, or Svelte, you get a setup with Wayfinder configured. The starter kits include:

* The Composer package `laravel/wayfinder`
* The NPM package `@laravel/vite-plugin-wayfinder`
* Plugin configuration already applied in `vite.config.js`
* Generated directories already added to `.gitignore`

You can also add it manually to an existing project following the steps above.

***

## Handling reserved words and conflicting method names

Controller methods with the same name as a JavaScript reserved word (like `delete` or `import`) get a `Method` suffix.

```ts theme={null}
// When the controller has a `delete` method
import { deleteMethod } from "@/actions/App/Http/Controllers/PostController";

deleteMethod(1); // { url: "/posts/1", method: "delete" }
```

***

## Current status (v0.1.x)

The current stable version is provided on the `v0.1.x` branch. As of March 2026, the latest version is **v0.1.15**.

### v0.1.x main changelog

| Version | Highlights                                                    |
| ------- | ------------------------------------------------------------- |
| v0.1.15 | Laravel 13 support, Blade view crash fix                      |
| v0.1.13 | Improved TypeScript strict compatibility for query parameters |
| v0.1.7  | Support for specifying URL default parameters on the frontend |
| v0.1.6  | Added Vite plugin                                             |
| v0.1.5  | PHP 8.2 support, support for cached routes                    |
| v0.1.0  | Initial release                                               |

***

## Next-gen features under development on the `next` branch

The `next` branch is developing a substantially expanded next version.

<Warning>
  The `next` branch can be installed with the `dev-next` constraint, but the API may change significantly. Not recommended for production.
</Warning>

```bash theme={null}
composer require laravel/wayfinder:dev-next
```

### Greatly expanded TypeScript generation

While v0.1.x targets only routes and controller actions, the next version generates all of the following as TypeScript.

```mermaid theme={null}
graph TD
    A["Laravel application"] --> B["wayfinder:generate"]
    B --> C["Routes & Actions<br>Route URL functions"]
    B --> D["Form Requests<br>Validation types"]
    B --> E["Eloquent Models<br>Model interfaces"]
    B --> F["PHP Enums<br>TypeScript constants"]
    B --> G["Inertia Page Props<br>Page prop types"]
    B --> H["Broadcast Channels<br>Channel types"]
    B --> I["Broadcast Events<br>Event payload types"]
    B --> J["Environment Variables<br>import.meta.env types"]
```

### Form Request TypeScript type generation

```php theme={null}
class StorePostRequest extends FormRequest
{
    public function rules(): array
    {
        return [
            'title'   => ['required', 'string', 'max:255'],
            'content' => ['required', 'string'],
            'tags'    => ['nullable', 'array'],
            'tags.*'  => ['string'],
        ];
    }
}
```

From the above form request, the following type is generated.

```ts theme={null}
export type Request = {
    title: string;
    content: string;
    tags?: string[] | null;
};
```

### Eloquent model type generation

```php theme={null}
class User extends Model
{
    protected $casts = [
        'email_verified_at' => 'datetime',
        'is_admin' => 'boolean',
    ];

    public function posts(): HasMany
    {
        return $this->hasMany(Post::class);
    }
}
```

From this model, types are generated in `types.d.ts`.

```ts theme={null}
export namespace App.Models {
    export type User = {
        id: number;
        name: string;
        email: string;
        email_verified_at: string | null;
        is_admin: boolean;
        posts: App.Models.Post[];
    };
}
```

### PHP Enum → TypeScript conversion

```php theme={null}
enum PostStatus: string
{
    case Draft = 'draft';
    case Published = 'published';
    case Archived = 'archived';
}
```

Both the type and constants are generated.

```ts theme={null}
// Type (types.d.ts)
export namespace App.Enums {
    export type PostStatus = "draft" | "published" | "archived";
}

// Constants (App/Enums/PostStatus.ts)
export const Draft = "draft";
export const Published = "published";
export const Archived = "archived";

export const PostStatus = { Draft, Published, Archived } as const;
```

### Output directory changes

In v0.1.x, output was split into three directories (`actions/`, `routes/`, `wayfinder/`). In the next version everything lives under `resources/js/wayfinder`.

```
resources/js/wayfinder/
├── App/Http/Controllers/
│   └── PostController.ts    # Action functions (actions/ is retired)
├── routes/
│   └── post.ts              # Named routes (same)
├── broadcast-channels.ts    # Broadcast channels
├── broadcast-events.ts      # Broadcast events
└── types.d.ts               # All type definitions (renamed from types.ts)
```

### Main differences from v0.1.x to next

* Import paths changed from `@/actions/...` to `@/wayfinder/...`
* The `--skip-actions`, `--skip-routes`, and `--with-form` flags are retired in favor of a configuration file
* `types.ts` is renamed to `types.d.ts`

***

## Summary

Laravel Wayfinder redesigns the "reference Laravel routes from JavaScript" feature Ziggy provided, in a TypeScript-first way. Its approach of importing generated functions delivers big improvements in type safety, IDE support, and tree shaking.

Even the current v0.1.x enables type-safe references to routes and controller actions and is adopted by default in Inertia-based Laravel starter kits. The next-branch version under development plans to evolve into a more comprehensive type-safety foundation that also generates Form Requests, Eloquent models, enums, and Inertia page props as TypeScript.

<Card title="Laravel Wayfinder GitHub" icon="github" href="https://github.com/laravel/wayfinder">
  Source code, CHANGELOG, and issues.
</Card>

<Card title="Vite Plugin Wayfinder" icon="bolt" href="https://github.com/laravel/vite-plugin-wayfinder">
  Detailed configuration options for the Vite plugin.
</Card>


## Related topics

- [Introducing the Blaze package](/en/blog/blaze-introduction.md)
- [Frontend](/en/frontend.md)
- [Getting started with Laravel Nightwatch](/en/blog/nightwatch-introduction.md)
- [Build SPAs with Inertia.js](/en/blog/inertia-introduction.md)
- [Laravel Boost](/en/boost.md)
