6 min read

Laravel Wayfinder's Next Beta: End-to-End Type Safety From PHP to TypeScript

Laravel Wayfinder's next beta generates typed TypeScript from your PHP for routes, models, enums, Inertia props, and broadcasts. Here is how it works.

Featured image for "Laravel Wayfinder's Next Beta: End-to-End Type Safety From PHP to TypeScript"

If you have ever shipped a route rename in Laravel and then spent twenty minutes hunting down a hardcoded URL string in a Vue component, you already understand the problem Wayfinder set out to solve. The original Wayfinder, which replaced Ziggy in Laravel’s Starter Kits, generated type-safe route helpers and stopped there. The next version, currently in public beta on the next branch, is a much larger swing. It reads your entire Laravel application and generates fully typed TypeScript for routes, form requests, Eloquent models, PHP enums, Inertia props, broadcast channels and events, and even your Vite environment variables. Your PHP becomes the single source of truth, and your frontend types follow automatically.

I have been testing it on a small Inertia project, and the experience is genuinely different from bolting on a code generator. Let me walk through what it does and the architecture that makes it work, because the design is as interesting as the feature list.

The Problem It Actually Solves

Modern Laravel apps live in two languages. PHP owns the routes, validation rules, models, and enums. TypeScript owns the components that consume all of it. Nothing forces those two sides to agree. You change a validation rule in a form request, your frontend form keeps the old shape, and the mismatch surfaces as a runtime bug or a confused QA ticket. Multiply that across routes, model attributes, enum cases, and broadcast payloads, and you have an entire category of bugs that exist purely because two type systems cannot see each other.

Wayfinder closes that gap by doing static analysis on your Laravel code and emitting TypeScript that mirrors it. When the PHP changes, you regenerate, and your editor immediately flags every frontend reference that no longer lines up. The errors move from production to compile time, which is exactly where you want them.

Surveyor and Ranger: The Engine Room

The interesting part is how the team got here. Rather than cramming everything into one package, they built two foundational libraries that Wayfinder sits on top of, and both are usable independently.

Surveyor is the static analysis layer. It walks your controllers, models, broadcast events, and other classes to extract their structure: methods, properties, return types, and relationships. It is mostly static, but it also inspects a few runtime pieces like container bindings and model configuration to get a complete picture.

Ranger sits on top of Surveyor and turns that raw analysis into clean data transfer objects. You register callbacks that fire as entities are discovered, and Ranger hands you structured DTOs to work with. Think of it as the translation layer between “here is everything in your codebase” and “here is a tidy object describing one route.”

Wayfinder is the consumer. It listens to Ranger, and when a route, model, or channel is discovered, it converts the DTO into TypeScript. The clean separation means Surveyor and Ranger can power other tooling entirely unrelated to TypeScript generation, which is a smart bit of ecosystem design.

What You Get in Practice

Routes are the headline. Instead of hardcoding URLs, you import generated helpers with full autocomplete for parameters and methods. Reference a route that does not exist and you get an error in your editor, not a 404 in the browser.

import { show, update } from '@/actions/App/Http/Controllers/PostController'

// Fully typed: TypeScript knows `show` needs an id
const url = show(42).url        // "/posts/42"
const method = update(42).method // "put"

Form requests are where it starts to feel like magic. Wayfinder parses the validation applied to a route and generates types that plug straight into Inertia’s useForm hook. Update a rule in PHP and the frontend form shape updates with it.

class StorePostRequest extends FormRequest
{
    public function rules(): array
    {
        return [
            'title' => ['required', 'string', 'max:255'],
            'status' => ['required', Rule::enum(PostStatus::class)],
            'published_at' => ['nullable', 'date'],
        ];
    }
}

On the frontend, the form is typed to match that exact structure, including the enum constraint on status.

Enums cross the boundary too. A PHP backed enum becomes a TypeScript representation automatically, so the select dropdown that lists your post statuses can never drift from the cases your model actually accepts.

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

Add a Scheduled case in PHP, regenerate, and it shows up in your TypeScript autocomplete immediately.

Models, Inertia props, and broadcasts round out the set. When you pass an Eloquent model through an Inertia prop, Wayfinder generates types for its attributes, relationships, and accessors, and the more specific your PHP types are, the sharper the generated TypeScript becomes. Shared props defined in your HandleInertiaRequests middleware get declaration-merging files so the usePage hook is fully typed without any manual setup. And if you use Laravel Echo, Wayfinder infers the payload shape from your broadcast events, so the data arriving over a WebSocket is typed rather than guessed at.

Jumping the Fence

The feature that made me sit up is cross-repository synchronization. Plenty of teams keep the Laravel API and the frontend in separate repos, which normally means copying type definitions by hand and hoping nobody forgets. Wayfinder can automate the whole loop. A GitHub Action in your API repo regenerates the types on change and opens a pull request against your frontend repo with the diff. You review it, merge it, pull, and the frontend is in sync without anyone touching a type definition manually.

In the launch demo, Joe Tannenbaum renamed a route from catalog to catalogs and added a new case to a theme enum in the API. Within seconds a PR appeared in the frontend repo with both changes. That is a workflow that genuinely removes a recurring source of integration pain.

Should You Use It Yet

This is what Joe calls an “uppercase B beta,” and the honesty is worth respecting. The current stable Wayfinder that ships in the Starter Kits handles routes and is production-ready. The next branch is the ambitious one, and the team is still working on a few rough edges: better support for classes implementing Arrayable and Jsonable (which matters for Eloquent resources and Spatie’s Laravel Data), performance tuning for large codebases where type generation can get memory-hungry, more accurate inference for complex Eloquent collections, and possible translation support down the road.

For a small to medium Inertia app, it is well worth installing on a branch and feeling out. For a large production codebase, I would test the generation step against your real models before committing to it, since that is exactly where the current performance work is focused. Either way, the feedback the team wants belongs in the GitHub repo, not on social media, where it can actually be tracked toward a stable 1.0.

If your daily friction is keeping PHP and TypeScript in agreement, Wayfinder is the most compelling answer the Laravel ecosystem has produced. It makes your backend the source of truth and lets the types simply be there, which is how this should have worked all along.

Sources