Zum Hauptinhalt springen

Was ist Wayfinder?

Laravel Wayfinder ist ein Paket, das ein Laravel-Backend reibungslos mit einem TypeScript-Frontend verbindet. Es erzeugt aus Controllern und Routen vollständig typisierte TypeScript-Funktionen, sodass Sie Laravel-Endpoints direkt als Funktion aus dem Frontend aufrufen können. Hartkodierte URLs, manuelles Verwalten von Route-Parametern und das händische Nachziehen von Backend-Änderungen entfallen komplett.
Wayfinder ist im Beta (aktuell v0.1.x). Bis zum v1.0.0-Release kann sich die API noch ändern. Alle relevanten Änderungen werden im CHANGELOG protokolliert.

Unterschiede zwischen Ziggy und Wayfinder

Was ist Ziggy?

Ziggy ist ein Route-Helper, der über Jahre im Laravel-Ökosystem verbreitet war. Er stellt Laravel-Routen JavaScript-seitig bereit und erzeugt URLs in der Form route('posts.show', { id: 1 }).

Warum Wayfinder ihn ablöst

Da Ziggy Route-Namen und Parameter als Strings behandelt, gab es Grenzen für TypeScript. Tippfehler in Route-Namen oder falsche Parameternamen fielen erst zur Laufzeit auf. Wayfinder ist TypeScript-first und erzeugt Controller-Methoden als importierbare Funktionen.
VergleichspunktZiggyWayfinder
Route-Referenzierungroute('posts.show', { id: 1 })import { show } from "@/actions/..."
Typsicherheiteingeschränkte Typdefinitionenvollständige TypeScript-Typen
IDE-Supportschwache Vervollständigungvolle Vervollständigung und Typprüfung
Tree-Shakingalle Routen ins Bundlenur genutzte Routen im Bundle
Zeitpunkt der ErzeugungLaufzeit-Injektionstatisch zur Buildzeit
In den Inertia-basierten Laravel-Starterkits (React, Vue, Svelte) ist Wayfinder standardmäßig aktiv.

Installation

1. Server-seitiges Paket per Composer installieren

composer require laravel/wayfinder

2. Vite-Plugin per NPM installieren

npm i -D @laravel/vite-plugin-wayfinder

3. Plugin in vite.config.js ergänzen

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(),
    ],
});
Mit dem Vite-Plugin werden bei laufendem Dev-Server die TypeScript-Dateien bei jeder Änderung an PHP- oder Route-Dateien automatisch neu erzeugt.

TypeScript-Definitionen erzeugen

Erzeugen Sie die TypeScript-Dateien mit dem Kommando wayfinder:generate.
php artisan wayfinder:generate
Standardmäßig werden unter resources/js drei Verzeichnisse erzeugt.
resources/js/
├── actions/         # Funktionen für Controller-Actions
│   └── App/Http/Controllers/
│       └── PostController.ts
├── routes/          # Funktionen für benannte Routen
│   └── post.ts
└── wayfinder/       # Typdefinitionen
    └── types.ts
Da die erzeugten Dateien bei jedem Build vollständig neu erzeugt werden, empfehlen wir, sie in .gitignore einzutragen. Schließen Sie die drei Verzeichnisse wayfinder, actions und routes gemeinsam aus.
Zum Ändern des Ausgabepfads verwenden Sie die Option --path.
php artisan wayfinder:generate --path=resources/js/api
Sie können auch nur Controller-Actions oder nur Routen erzeugen.
php artisan wayfinder:generate --skip-actions  # nur Routen erzeugen
php artisan wayfinder:generate --skip-routes   # nur Actions erzeugen

Grundlegende Verwendung

Actions importieren und nutzen

Beispiel: URL-Erzeugung für die show-Methode von PostController.
import { show } from "@/actions/App/Http/Controllers/PostController";

show(1);
// { url: "/posts/1", method: "get" }
Wenn nur die URL benötigt wird, verwenden Sie .url().
show.url(1); // "/posts/1"
Auch eine bestimmte HTTP-Methode ist möglich.
show.head(1); // { url: "/posts/1", method: "head" }

Übergabe von Parametern

Wayfinder-Funktionen akzeptieren verschiedene Parameterformen.
import { update } from "@/actions/App/Http/Controllers/PostController";

// Einzelner Parameter
show(1);
show({ id: 1 });

// Mehrere Parameter
update([1, 2]);
update({ post: 1, author: 2 });
update({ post: { id: 1 }, author: { id: 2 } });
Wenn die Route ein Key-Binding vorgibt (/posts/{post:slug}), können Sie diesen Wert nutzen.
// Route lautet /posts/{post:slug}
show("my-new-post");
show({ slug: "my-new-post" });

Vollständigen Controller importieren

Sie können auch den gesamten Controller importieren und Methoden darauf aufrufen.
import PostController from "@/actions/App/Http/Controllers/PostController";

PostController.show(1);
PostController.index();
Beim Import des gesamten Controllers greift Tree-Shaking nicht, und alle Actions landen im Bundle. Einzelimporte halten die finale Bundle-Größe kleiner.

Invokable Controller

Bei Invokable Controllern rufen Sie die importierte Funktion direkt auf.
import StorePostController from "@/actions/App/Http/Controllers/StorePostController";

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

Benannte Routen importieren

Für den Zugriff über Route-Namen verwenden Sie die Dateien unter routes/.
import { show } from "@/routes/post";

// Wenn der Route-Name `post.show` lautet
show(1); // { url: "/posts/1", method: "get" }

Query-Parameter

Alle Wayfinder-Funktionen akzeptieren query für zusätzliche Query-Parameter.
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" }
Zum Mergen mit den aktuellen Query-Parametern der URL nutzen Sie mergeQuery.
// Aktuelle 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"

// Zum Entfernen eines Parameters null angeben
show.url(1, { mergeQuery: { sort_by: null } });
// "/posts/1?page=1&q=shirt"

Form-Varianten

Für den Einsatz in klassischen HTML-Formularen erzeugen Sie mit --with-form und verwenden die .form-Variante.
php artisan wayfinder:generate --with-form
import { store, update } from "@/actions/App/Http/Controllers/PostController";

// Beispiel in React
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>
);

Kombination aus Inertia und Wayfinder

Kombiniert mit den Formular-Helpern von Inertia versenden Sie Formulare, ohne einen einzigen URL-String zu schreiben.
import { useForm } from "@inertiajs/react";
import { store } from "@/actions/App/Http/Controllers/PostController";

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

form.submit(store()); // Sendet POST /posts
Auch mit der Link-Komponente funktioniert es analog.
import { Link } from "@inertiajs/react";
import { show } from "@/actions/App/Http/Controllers/PostController";

const Nav = () => (
    <Link href={show(1)}>Beitrag ansehen</Link>
);

Übernahme im Starterkit

Wenn Sie mit laravel new ein neues Projekt anlegen und React, Vue oder Svelte wählen, erhalten Sie eine Konfiguration, in der Wayfinder automatisch eingerichtet ist. Das Starterkit enthält:
  • Composer-Paket laravel/wayfinder
  • NPM-Paket @laravel/vite-plugin-wayfinder
  • Plugin-Konfiguration in vite.config.js
  • Eintrag der generierten Verzeichnisse in .gitignore
Auch die manuelle Einführung in bestehende Projekte ist mit obigen Schritten möglich.

Behandlung von Reserved Words

Für Controller-Methoden mit dem Namen eines JavaScript-Schlüsselworts (z. B. delete, import) wird das Suffix Method angehängt.
// Wenn der Controller eine delete-Methode besitzt
import { deleteMethod } from "@/actions/App/Http/Controllers/PostController";

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

Aktueller Stand (v0.1.x)

Die aktuelle Stable-Version wird über den Branch v0.1.x bereitgestellt. Stand März 2026 ist die neueste Version v0.1.15.

Wichtige Änderungen der v0.1.x-Reihe

VersionWichtigste Inhalte
v0.1.15Laravel-13-Support, Fix eines Blade-View-Crashes
v0.1.13Bessere TypeScript-Strict-Kompatibilität bei Query-Parametern
v0.1.7URL-Default-Parameter frontendseitig festlegbar
v0.1.6Vite-Plugin ergänzt
v0.1.5PHP-8.2-Support, Unterstützung für Route-Cache
v0.1.0Erste Veröffentlichung

Im next-Branch entwickelte Next-Gen-Features

Im Branch next wird die nächste Version entwickelt, die den Funktionsumfang gegenüber v0.1.x deutlich erweitert.
Der next-Branch lässt sich per dev-next-Constraint installieren, doch die API kann sich stark ändern. Der Einsatz in Produktion ist nicht empfohlen.
composer require laravel/wayfinder:dev-next

Deutlich erweiterter TypeScript-Umfang

Während v0.1.x nur Routen und Controller-Actions abdeckt, erzeugt die nächste Version alle folgenden Elemente als TypeScript.

Typen aus FormRequests

class StorePostRequest extends FormRequest
{
    public function rules(): array
    {
        return [
            'title'   => ['required', 'string', 'max:255'],
            'content' => ['required', 'string'],
            'tags'    => ['nullable', 'array'],
            'tags.*'  => ['string'],
        ];
    }
}
Aus diesem FormRequest werden folgende Typen erzeugt.
export type Request = {
    title: string;
    content: string;
    tags?: string[] | null;
};

Typen aus Eloquent-Modellen

class User extends Model
{
    protected $casts = [
        'email_verified_at' => 'datetime',
        'is_admin' => 'boolean',
    ];

    public function posts(): HasMany
    {
        return $this->hasMany(Post::class);
    }
}
Aus dem Modell werden Typen in types.d.ts erzeugt.
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 zu TypeScript

enum PostStatus: string
{
    case Draft = 'draft';
    case Published = 'published';
    case Archived = 'archived';
}
Sowohl Typen als auch Konstanten werden generiert.
// Typdefinition (types.d.ts)
export namespace App.Enums {
    export type PostStatus = "draft" | "published" | "archived";
}

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

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

Änderungen am Ausgabeverzeichnis

In v0.1.x war die Ausgabe auf actions/, routes/ und wayfinder/ aufgeteilt; in der nächsten Version wird alles unter resources/js/wayfinder gebündelt.
resources/js/wayfinder/
├── App/Http/Controllers/
│   └── PostController.ts    # Action-Funktionen (actions/ entfällt)
├── routes/
│   └── post.ts              # Benannte Routen (gleich)
├── broadcast-channels.ts    # Broadcast-Channels
├── broadcast-events.ts      # Broadcast-Events
└── types.d.ts               # Alle Typdefinitionen (statt types.ts)

Wichtigste Änderungen von v0.1.x auf next

  • Import-Pfad wechselt von @/actions/... zu @/wayfinder/...
  • Flags --skip-actions, --skip-routes, --with-form entfallen und wandern in die Konfiguration
  • types.ts heißt jetzt types.d.ts

Fazit

Laravel Wayfinder ist die TypeScript-first-Neukonzeption der Ziggy-Funktionalität „Laravel-Routen aus JavaScript nutzen”. Der Import erzeugter Funktionen bringt bei Typsicherheit, IDE-Support und Tree-Shaking erhebliche Vorteile. Bereits v0.1.x ermöglicht typsichere Referenzen auf Routen und Controller-Actions und ist Standardteil der Inertia-basierten Laravel-Starterkits. Die im next-Branch entwickelte nächste Version wird zu einem umfassenderen Typ-Fundament ausgebaut, das FormRequests, Eloquent-Modelle, Enums und Inertia-Page-Props als TypeScript erzeugt.

GitHub-Repository Laravel Wayfinder

Quellcode, CHANGELOG und Issues finden Sie hier.

Vite Plugin Wayfinder

Details zu den Konfigurationsoptionen des Vite-Plugins.
Zuletzt geändert am 13. Juli 2026