Если вы пишете фронтенд на TypeScript поверх Laravel, то наверняка сталкивались с вечной болью: URL-адреса маршрутов приходится либо хардкодить строками, либо тащить через Ziggy и надеяться, что имена маршрутов на бэкенде и фронтенде не разъедутся. Переименовали контроллер или поменяли сегмент URI — и узнаёте об этом только в рантайме, когда что-то ломается. Laravel Wayfinder решает эту проблему радикально: он генерирует полностью типизированные TypeScript-функции для ваших контроллеров и маршрутов, которые можно импортировать и вызывать как обычные функции. В этой статье разберём, как установить Wayfinder, настроить генерацию и применять его на практике.
Что такое Wayfinder и какую проблему он решает
Wayfinder — это официальный first-party пакет от команды Laravel, который связывает бэкенд на PHP и фронтенд на TypeScript без лишнего трения. Он анализирует ваше приложение и автоматически создаёт TypeScript-эквиваленты для маршрутов и методов контроллеров. В результате вы вызываете эндпоинты Laravel прямо в клиентском коде, как любую другую функцию — с автодополнением, проверкой типов параметров и без ручной синхронизации.
На момент написания статьи Wayfinder находится в стадии Beta (последний релиз — v0.1.20 от мая 2026 года), и API может меняться вплоть до выхода стабильной версии v1.0.0. Тем не менее пакет уже достаточно зрелый: начиная с Laravel 12 он заменил Ziggy в официальных стартовых наборах (starter kits). Так что знакомиться с ним стоит уже сейчас.
Установка
Сначала ставим сам пакет через Composer:
composer require laravel/wayfinder
Дальше нужен Vite-плагин, чтобы определения автоматически генерировались при сборке и при изменении файлов во время работы dev-сервера. Ставим его через NPM:
npm i -D @laravel/vite-plugin-wayfinder
После этого подключаем плагин в vite.config.js:
import { wayfinder } from "@laravel/vite-plugin-wayfinder";
export default defineConfig({
plugins: [
wayfinder(),
// ...
],
});
Теперь при каждом запуске сборки и при изменении маршрутов или контроллеров Wayfinder будет перегенерировать TypeScript-определения автоматически.
Генерация определений
За генерацию отвечает Artisan-команда:
php artisan wayfinder:generate
По умолчанию Wayfinder создаёт файлы в трёх директориях внутри resources/js: wayfinder, actions и routes. Базовый путь можно переопределить:
php artisan wayfinder:generate --path=resources/js/wayfinder
Если вам не нужны определения для методов контроллеров или для маршрутов, отдельные части генерации можно пропустить:
php artisan wayfinder:generate --skip-actions
php artisan wayfinder:generate --skip-routes
Сгенерированные директории wayfinder, actions и routes можно смело добавлять в .gitignore — они полностью пересоздаются на каждой сборке, так что хранить их в репозитории нет смысла.
Использование в клиентском коде
Функции Wayfinder возвращают объект с готовым URL и HTTP-методом по умолчанию:
import { show } from "@/actions/App/Http/Controllers/PostController";
show(1); // { url: "/posts/1", method: "get" }
Если нужен только URL или другой метод из объявленных на сервере, можно вызвать дополнительные методы на сгенерированной функции:
import { show } from "@/actions/App/Http/Controllers/PostController";
show.url(1); // "/posts/1"
show.head(1); // { url: "/posts/1", method: "head" }
Аргументы можно передавать в разных формах — это удобно, когда у вас на руках то отдельный id, то целый объект модели:
import { show, update } from "@/actions/App/Http/Controllers/PostController";
// Один параметр...
show(1);
show({ id: 1 });
// Несколько параметров...
update([1, 2]);
update({ post: 1, author: 2 });
update({ post: { id: 1 }, author: { id: 2 } });
Если в маршруте задан ключ привязки (например, /posts/{post:slug}), Wayfinder это распознает и позволит передать значение через свойство объекта:
import { show } from "@/actions/App/Http/Controllers/PostController";
// Маршрут /posts/{post:slug}...
show("my-new-post");
show({ slug: "my-new-post" });
Для invokable-контроллеров импортированную функцию можно вызывать напрямую, а при импорте контроллера целиком — обращаться к его методам как к свойствам объекта:
import StorePostController from "@/actions/App/Http/Controllers/StorePostController";
StorePostController();
import PostController from "@/actions/App/Http/Controllers/PostController";
PostController.show(1);
Обратите внимание: при импорте контроллера целиком он не подвергается tree-shaking, и в финальный бандл попадут все его экшены. Если важен размер сборки, импортируйте отдельные методы.
Именованные маршруты и формы
Помимо экшенов контроллеров, Wayfinder умеет генерировать методы и для именованных маршрутов:
import { show } from "@/routes/post";
// Именованный маршрут `post.show`...
show(1); // { url: "/posts/1", method: "get" }
Если приложение использует обычную отправку HTML-форм, Wayfinder поможет и здесь. Сначала включаем генерацию form-вариантов:
php artisan wayfinder:generate --with-form
Теперь у каждой функции появляется вариант .form, который раскладывается в атрибуты тега <form>. Метод вроде PATCH автоматически подставляется через скрытое поле _method:
import { store, update } from "@/actions/App/Http/Controllers/PostController";
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>
);
Query-параметры и интеграция с Inertia
Каждый метод Wayfinder принимает финальный необязательный аргумент options, куда можно передать объект query для добавления query-параметров:
import { show } from "@/actions/App/Http/Controllers/PostController";
const options = { query: { page: 1, sort_by: "name" } };
show(1, options); // { url: "/posts/1?page=1&sort_by=name", method: "get" }
show.url(1, options); // "/posts/1?page=1&sort_by=name"
Если нужно слить новые параметры с уже существующими в URL, используйте mergeQuery. Чтобы удалить параметр, задайте его значение как null или undefined.
Отдельно радует связка с Inertia. Результат метода Wayfinder можно передать прямо в submit у useForm — URL и метод подставятся сами:
import { useForm } from "@inertiajs/react";
import { store } from "@/actions/App/Http/Controllers/PostController";
const form = useForm({ name: "My Big Post" });
form.submit(store()); // POST на `/posts`...
Точно так же Wayfinder работает с компонентом Link из Inertia — просто передайте результат функции в проп href.
Важный нюанс при деплое
Wayfinder читает маршруты из зарегистрированного роутера. Если Laravel поднимается с закешированной таблицей маршрутов от предыдущего релиза, генерация отработает по устаревшим данным: новые маршруты молча не попадут в директории routes/ и actions/, а Vite упадёт с ошибкой вида Could not load resources/js/routes/<name>. Если ваш скрипт деплоя выполняет php artisan optimize или route:cache, обязательно сбросьте кеш перед сборкой:
php artisan route:clear
npm run build
Итоги
Wayfinder убирает целый класс ошибок, связанных с рассинхронизацией маршрутов между бэкендом и фронтендом. Вместо хрупких строк с URL вы получаете типизированные функции с автодополнением, которые ломают сборку (а не прод), если на бэкенде что-то изменилось. Установка занимает пару команд, интеграция с Vite и Inertia сделана аккуратно, а поддержка форм и query-параметров закрывает большинство реальных сценариев. Единственное, о чём стоит помнить — пакет пока в Beta, поэтому перед обновлением версий заглядывайте в changelog. Но учитывая, что Wayfinder уже стал частью официальных стартовых наборов Laravel 12, инвестиция в его освоение точно окупится.