Vai al contenuto principale

Cos’è Wayfinder

Laravel Wayfinder è un pacchetto che collega senza attriti il backend Laravel al frontend TypeScript. Genera automaticamente funzioni TypeScript completamente tipizzate a partire da controller e rotte, così puoi chiamare gli endpoint Laravel direttamente come funzioni dal codice frontend. Nessuna hardcoding di URL, nessuna gestione manuale dei parametri di rotta, nessuna sincronizzazione manuale dei cambiamenti backend: tutto questo diventa superfluo.
Wayfinder è in Beta (attualmente v0.1.x). Prima del rilascio di v1.0.0 l’API può cambiare. Tutte le modifiche importanti sono registrate nel CHANGELOG.

Differenze tra Ziggy e Wayfinder

Cos’è Ziggy

Ziggy è un route helper usato per anni nell’ecosistema Laravel. Espone al lato JavaScript le definizioni di rotta di Laravel, permettendo di generare URL con route('posts.show', { id: 1 }).

Perché è stato sostituito da Wayfinder

Ziggy tratta nome della rotta e parametri come stringhe e ha limiti nell’interazione con TypeScript. Refusi nei nomi di rotta o nomi di parametri errati diventano errori solo a runtime. Wayfinder è progettato TypeScript-first e genera i metodi dei controller come funzioni importabili.
ConfrontoZiggyWayfinder
Riferimento alla rottaroute('posts.show', { id: 1 })import { show } from "@/actions/..."
Type-safetyDefinizioni di tipo limitateTipi TypeScript completi
Supporto IDECompletamento deboleCompletamento e type check completi
Tree-shakingInclude tutte le rotte nel bundleSolo le rotte usate nel bundle
Momento di generazioneIniezione a runtimeGenerazione statica in build
Negli starter kit Laravel basati su Inertia (React, Vue, Svelte), Wayfinder è adottato come standard.

Installazione

1. Installazione del pacchetto lato server con Composer

composer require laravel/wayfinder

2. Installazione del plugin Vite con NPM

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

3. Aggiunta del plugin in vite.config.js

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(),
    ],
});
Aggiungendo il plugin Vite, mentre il server di sviluppo è attivo, i file TypeScript vengono rigenerati automaticamente ogni volta che si modificano i file PHP o i file di rotta.

Generazione dei file di definizione TypeScript

Con il comando wayfinder:generate puoi generare i file TypeScript.
php artisan wayfinder:generate
Per impostazione predefinita vengono generate tre directory sotto resources/js.
resources/js/
├── actions/         # Funzioni per le azioni dei controller
│   └── App/Http/Controllers/
│       └── PostController.ts
├── routes/          # Funzioni per le rotte con nome
│   └── post.ts
└── wayfinder/       # File di definizione di tipo
    └── types.ts
I file generati vengono ricreati completamente a ogni build, quindi è consigliato aggiungerli a .gitignore. Escludi le tre directory wayfinder, actions e routes insieme.
Se vuoi cambiare la destinazione di output, usa l’opzione --path.
php artisan wayfinder:generate --path=resources/js/api
Puoi anche generare solo le azioni dei controller o solo le rotte.
php artisan wayfinder:generate --skip-actions  # Genera solo le rotte
php artisan wayfinder:generate --skip-routes   # Genera solo le azioni

Uso di base

Importare e usare un’azione

Esempio per generare l’URL corrispondente al metodo show di PostController.
import { show } from "@/actions/App/Http/Controllers/PostController";

show(1);
// { url: "/posts/1", method: "get" }
Se ti serve solo l’URL, usa .url().
show.url(1); // "/posts/1"
Puoi anche specificare un metodo HTTP.
show.head(1); // { url: "/posts/1", method: "head" }

Come passare i parametri

Le funzioni Wayfinder accettano parametri in varie forme.
import { update } from "@/actions/App/Http/Controllers/PostController";

// Parametro singolo
show(1);
show({ id: 1 });

// Parametri multipli
update([1, 2]);
update({ post: 1, author: 2 });
update({ post: { id: 1 }, author: { id: 2 } });
Se la rotta specifica un key binding (/posts/{post:slug}), puoi usarne il valore.
// Se la rotta è /posts/{post:slug}
show("my-new-post");
show({ slug: "my-new-post" });

Importare l’intero controller

Puoi anche importare l’intero controller e chiamarne i metodi.
import PostController from "@/actions/App/Http/Controllers/PostController";

PostController.show(1);
PostController.index();
Importando l’intero controller si perde il tree-shaking e tutte le azioni finiscono nel bundle. Importare singolarmente mantiene più piccolo il bundle finale.

Controller a singola azione

I controller a singola azione (Invokable Controller) si chiamano direttamente come funzione importata.
import StorePostController from "@/actions/App/Http/Controllers/StorePostController";

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

Importazione delle rotte con nome

Per accedere per nome di rotta, usa i file sotto routes/.
import { show } from "@/routes/post";

// Se il nome della rotta è `post.show`
show(1); // { url: "/posts/1", method: "get" }

Parametri di query

Tutte le funzioni Wayfinder possono aggiungere parametri di query tramite l’opzione query.
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" }
Per fare merge con i parametri di query dell’URL corrente usa mergeQuery.
// URL corrente: /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"

// Per rimuovere un parametro passa null
show.url(1, { mergeQuery: { sort_by: null } });
// "/posts/1?page=1&q=shirt"

Variante Form

Per usarlo nei form HTML tradizionali, genera con l’opzione --with-form e usa la variante .form.
php artisan wayfinder:generate --with-form
import { store, update } from "@/actions/App/Http/Controllers/PostController";

// Esempio 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>
);

Combinare Inertia e Wayfinder

Combinando gli helper form di Inertia con Wayfinder, puoi inviare form senza scrivere alcuna stringa URL.
import { useForm } from "@inertiajs/react";
import { store } from "@/actions/App/Http/Controllers/PostController";

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

form.submit(store()); // Invio a POST /posts
Funziona allo stesso modo anche con il componente Link.
import { Link } from "@inertiajs/react";
import { show } from "@/actions/App/Http/Controllers/PostController";

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

Adozione negli starter kit

Se crei un nuovo progetto con laravel new scegliendo React, Vue o Svelte, viene fornita una configurazione con Wayfinder già impostato. Negli starter kit sono inclusi:
  • Pacchetto Composer laravel/wayfinder
  • Pacchetto NPM @laravel/vite-plugin-wayfinder
  • Configurazione del plugin già impostata in vite.config.js
  • Directory generate già aggiunte a .gitignore
Puoi introdurlo manualmente in progetti esistenti seguendo la procedura sopra.

Gestione di nomi di metodo in conflitto con parole riservate

Ai metodi di controller con lo stesso nome di parole riservate JavaScript come delete o import viene aggiunto il suffisso Method.
// Se il controller ha un metodo delete
import { deleteMethod } from "@/actions/App/Http/Controllers/PostController";

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

Stato attuale (v0.1.x)

La versione stabile attuale è fornita dal branch v0.1.x. Al marzo 2026 l’ultima versione è v0.1.15.

Principali modifiche della serie v0.1.x

VersioneContenuto principale
v0.1.15Compatibilità con Laravel 13, fix di crash con view Blade
v0.1.13Miglioramenti di compatibilità strict TypeScript per i parametri di query
v0.1.7Supporto alla specifica frontend dei parametri di default URL
v0.1.6Aggiunta del plugin Vite
v0.1.5Supporto a PHP 8.2, compatibilità con rotte in cache
v0.1.0Rilascio iniziale

Funzionalità di nuova generazione in sviluppo nel branch next

Nel branch next è in sviluppo la prossima versione, con funzionalità notevolmente estese rispetto all’attuale v0.1.x.
Il branch next è installabile con il vincolo dev-next, ma l’API può cambiare significativamente. L’uso in produzione non è raccomandato.
composer require laravel/wayfinder:dev-next

Estensione notevole della portata del TypeScript generato

Mentre v0.1.x copre solo rotte e azioni dei controller, la prossima versione genera tutto quanto segue come TypeScript.

Generazione di tipi TypeScript per Form Request

class StorePostRequest extends FormRequest
{
    public function rules(): array
    {
        return [
            'title'   => ['required', 'string', 'max:255'],
            'content' => ['required', 'string'],
            'tags'    => ['nullable', 'array'],
            'tags.*'  => ['string'],
        ];
    }
}
Da questa Form Request viene generato il seguente tipo.
export type Request = {
    title: string;
    content: string;
    tags?: string[] | null;
};

Generazione di tipi per modelli Eloquent

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

    public function posts(): HasMany
    {
        return $this->hasMany(Post::class);
    }
}
Da questo modello i tipi vengono generati in types.d.ts.
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[];
    };
}

Conversione degli Enum PHP in TypeScript

enum PostStatus: string
{
    case Draft = 'draft';
    case Published = 'published';
    case Archived = 'archived';
}
Vengono generati sia il tipo sia le costanti.
// Definizione di tipo (types.d.ts)
export namespace App.Enums {
    export type PostStatus = "draft" | "published" | "archived";
}

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

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

Modifica della directory di output

In v0.1.x c’erano tre directory separate (actions/, routes/, wayfinder/), mentre nella prossima versione tutto è raccolto sotto resources/js/wayfinder.
resources/js/wayfinder/
├── App/Http/Controllers/
│   └── PostController.ts    # Funzioni azione (actions/ eliminata)
├── routes/
│   └── post.ts              # Rotte con nome (uguale)
├── broadcast-channels.ts    # Canali broadcast
├── broadcast-events.ts      # Eventi broadcast
└── types.d.ts               # Tutte le definizioni di tipo (modificato da types.ts)

Principali cambiamenti da v0.1.x a next

  • Il path di import cambia da @/actions/... a @/wayfinder/...
  • I flag --skip-actions, --skip-routes, --with-form vengono eliminati e migrati nel file di configurazione
  • types.ts diventa types.d.ts

Conclusioni

Laravel Wayfinder è la riprogettazione TypeScript-first della funzionalità “riferire le rotte Laravel da JavaScript” offerta da Ziggy. Con l’approccio di importare funzioni generate, migliora notevolmente su tutti i fronti: type-safety, supporto IDE e tree-shaking. Già oggi con v0.1.x realizza il riferimento type-safe di rotte e azioni dei controller ed è adottato come standard negli starter kit Laravel basati su Inertia. Nella prossima versione in sviluppo nel branch next diventerà una base type-safe ancora più completa, generando come TypeScript anche Form Request, modelli Eloquent, Enum e prop pagina Inertia.

Laravel Wayfinder GitHub

Codice sorgente, CHANGELOG e issue qui.

Vite Plugin Wayfinder

Dettagli sulle opzioni di configurazione del plugin Vite qui.
Ultima modifica il 13 luglio 2026