Overview: The Quest for End-to-End Type Safety For years, developers building with Laravel have faced a persistent friction point: the communication gap between the PHP backend and the JavaScript or TypeScript frontend. While PHP has evolved into a robust, type-heavy language, those types often vanish the moment data hits the network. You might define a precise `Product` model or a strict `Enum` in Laravel, but your frontend remains blissfully unaware, forced to rely on manual type definitions that inevitably drift out of sync with the server. Laravel Wayfinder solves this by acting as an automated bridge. It doesn't just generate static files; it performs deep analysis of your application to extract routes, Inertia.js props, validation rules, and broadcast events, turning them into fully-typed TypeScript helpers. This ensures that a change in your Laravel controller immediately triggers a type error in your Vue.js or React components if the data contract is broken. It brings the "all-in-one" type safety of Livewire to the world of modern SPAs and separated repositories. Prerequisites To get the most out of this tutorial, you should be comfortable with: * **Laravel 10+**: Basic knowledge of routing, controllers, and Form Requests. * **Modern Frontend Frameworks**: Familiarity with React or Vue.js, specifically using Vite as a build tool. * **TypeScript Basics**: Understanding how interfaces and types provide editor autocomplete and build-time safety. * **GitHub Actions**: Basic knowledge of CI/CD workflows if you plan to sync types across separate repositories. Key Libraries & Tools * **Surveyor**: A "mostly static" analysis tool that inspects your PHP classes, methods, and bindings to extract raw metadata about your app. * **Ranger**: A layer above Surveyor that consumes raw data and transforms it into rich, digestible Data Transfer Objects (DTOs). * **Wayfinder Vite Plugin**: The client-side companion that watches for backend changes and triggers the regeneration of TypeScript definitions in real-time. * **Laravel Echo**: When combined with Wayfinder, it provides type-safe event broadcasting payloads. Code Walkthrough: Implementing Type-Safe Contracts 1. The Vite Integration Everything starts with the Vite configuration. You must register the Wayfinder plugin to enable the watcher that tracks your PHP files. ```javascript import { defineConfig } from 'vite'; import laravel from 'laravel-vite-plugin'; import wayfinder from 'wayfinder-vite-plugin'; export default defineConfig({ plugins: [ laravel(['resources/js/app.ts']), wayfinder({ // Patterns of files to watch for changes watch: ['app/Http/Controllers/**', 'app/Models/**'] }), ], }); ``` 2. Auto-Generating Shared Props In an Inertia.js application, shared props (like the current user or flash messages) are notoriously difficult to type. Wayfinder analyzes your `HandleInertiaRequests` middleware to sync these automatically. ```php // app/Http/Middleware/HandleInertiaRequests.php public function share(Request $request): array { return array_merge(parent::share($request), [ 'auth' => [ 'user' => $request->user(), ], 'is_admin' => (bool) $request->user()?->admin, ]); } ``` On the frontend, Wayfinder performs **declaration merging** so that the `usePage` hook knows exactly what is available: ```typescript import { usePage } from '@inertiajs/react'; const { props } = usePage(); // TypeScript knows 'is_admin' exists and is a boolean if (props.is_admin) { console.log("Access granted"); } ``` 3. Validation via Form Requests One of the most powerful features in the latest beta is the extraction of validation rules. When you type-hint a `FormRequest` in your controller, Wayfinder generates a matching TypeScript interface. ```php // app/Http/Requests/ProductUpdateRequest.php public function rules(): array { return [ 'name' => 'required|string', 'price' => 'required|numeric|min:0', 'description' => 'nullable|string', ]; } ``` Wayfinder converts these rules into a type you can pass to Inertia's `useForm` hook, preventing you from sending the wrong data types to the server. ```typescript import { useForm } from '@inertiajs/react'; import { ProductUpdateRequest } from '@/types/generated'; const form = useForm<ProductUpdateRequest>({ name: '', price: 0, description: null, }); ``` Syntax Notes: Specificity Matters Wayfinder relies on the clarity of your PHP code. The more specific your types are in Laravel, the better the TypeScript output. For example, if a controller method returns a collection, use PHP DocBlocks or native type hints to specify the model within that collection. Wayfinder effectively "reads" your intent. If you mark a property as `nullable` in a Form Request, it will correctly append `| null` to the generated TypeScript definition. Practical Example: Jumping the Fence What happens if your Laravel backend and Vue.js frontend live in separate repositories? This is the "Jump the Fence" scenario. You can use a GitHub Actions workflow to keep them in sync. When you commit a change to the Laravel API, the workflow runs Wayfinder, generates the new types, and automatically opens a Pull Request against the frontend repository. This workflow ensures that the frontend team is immediately notified when a route changes or a new field is added to an API response. It turns a manual communication task into a fail-safe automated process. Tips & Gotchas * **Cashing Issues**: During beta, the internal cache of Surveyor can occasionally become corrupted. If your types aren't reflecting your PHP changes, try clearing your app cache or restarting the Vite dev server. * **Performance in Large Apps**: Because Wayfinder performs static analysis across your entire codebase, very large applications might experience a slight delay (a few seconds) between saving a PHP file and the TypeScript server picking up the change. * **Tree Shaking**: Unlike older tools that exported every route into a global object, Wayfinder exports individual route helpers. This allows modern bundlers to "tree-shake" away any routes that aren't actually imported in your frontend code, keeping your production bundles lean. * **Eloquent Resources**: Full support for complex `JsonResource` transformations is still in active development. For the most reliable results, stick to `arrayable` and `jsonable` objects for now.
Tim McDonald
People
The Laravel channel features Tim McDonald in 7 mentions, framing him positively alongside technical discussions in videos like "Laravel Nightwatch AMA" and "Wayfinder."
- Jan 10, 2026
- Jun 20, 2025
- Apr 4, 2025
- Aug 29, 2024
- Jul 27, 2023
Overview: Elevating the Command Line Experience For years, PHP developers have relied on the Symfony Console component to handle command-line interactions. While functional, these traditional prompts often feel static and lack the polish found in modern JavaScript ecosystems. Laravel Prompts, introduced by Jess Archer, changes this by providing a suite of functions designed to create beautiful, user-friendly CLI inputs. This package isn't just about aesthetics; it introduces critical features like live validation, searchable selects, and multi-select capabilities that were previously cumbersome to implement in PHP. By focusing on the developer experience (DX), Laravel now offers a command-line interface that feels as responsive and intuitive as a modern web form. This matters because well-designed CLI tools reduce user error and lower the barrier to entry for complex administrative tasks. Prerequisites To follow this tutorial, you should have a solid grasp of the following: * **PHP 8.x**: The package utilizes modern PHP features like named parameters and arrow functions. * **Laravel Artisan**: Familiarity with creating and running console commands via `php artisan`. * **Composer**: Knowledge of how to manage PHP dependencies. Key Libraries & Tools * **Laravel Prompts**: The core package providing the interactive functional API. * **Artisan**: Laravel's built-in command-line interface. * **ANSI Escape Sequences**: The underlying technology used to manipulate the terminal cursor and colors. Code Walkthrough: Implementing Core Features Unlike the class-based approach in Symfony, Laravel Prompts uses namespaced functions. This keeps your code clean and expressive. Text Input and Validation The `text` function replaces the old `$this->ask()` method. It supports placeholders and sophisticated validation logic. ```python use function laravel\prompts\text; $name = text( label: 'What is your name?', placeholder: 'E.g. Taylor Otwell', required: true, validate: fn (string $value) => match (true) { strlen($value) < 3 => 'The name must be at least 3 characters.', default => null } ); ``` In this snippet, we use **named parameters** to define the prompt. The `validate` callback allows for real-time feedback. Once a user hits enter and triggers an error, the prompt enters a **live validation mode**, updating the error message as the user types. Interactive Selects and Search Selecting from a list is a common requirement. The `select` and `search` functions offer a significant upgrade over numeric-based choices. ```python use function laravel\prompts\select; use function laravel\prompts\search; // Basic Select $role = select( label: 'What role should the user have?', options: [ 'admin' => 'Administrator', 'editor' => 'Editor', 'viewer' => 'Viewer', ] ); // Eloquent-powered Search $id = search( label: 'Search for a user to email', options: fn (string $value) => strlen($value) > 0 ? User::where('name', 'like', "%{$value}%")->pluck('name', 'id')->all() : [] ); ``` The `search` function is particularly powerful. It accepts a callback that can query a database in real-time as the user types, returning an associative array where the key is the value returned to your code and the value is the label shown to the user. Syntax Notes: Named Parameters and Traits Laravel Prompts leans heavily into **PHP 8's named parameters**. Because these functions often have many optional arguments (like `default`, `placeholder`, and `hint`), named parameters allow you to skip the ones you don't need without passing multiple `null` values. When integrating these into existing Laravel commands, you should implement the `PromptsForMissingInput` interface. This allows Artisan to automatically trigger prompts if a required argument is missing, rather than simply throwing a "Not enough arguments" error. This creates a seamless fallback for users who forget the specific syntax of your command. Practical Examples: Artisan Integration Beyond simple scripts, Laravel Prompts is now deeply integrated into the Laravel framework itself. For instance, when running `php artisan make:model`, if you omit the name, the framework uses a text prompt with a placeholder suggesting conventions like "Flight". Another excellent use case is the `vendor:publish` command. Previously, this generated a massive, hard-to-read list. Now, it uses the `select` function with a defined **scroll height**, allowing users to navigate hundreds of options using arrow keys or even `hjkl` for Vim enthusiasts. This demonstrates the package's ability to handle scale while maintaining a clean UI. Tips & Gotchas * **Windows Compatibility**: Laravel Prompts relies on specific terminal features. If you are on Windows and not using **WSL (Windows Subsystem for Linux)**, the package will automatically fall back to standard Symfony prompts. Always test your commands in the environment your team uses. * **Cancelation Handling**: When a user hits `Ctrl+C`, the prompt enters a "canceled" state, often striking through the input. Ensure your logic accounts for cases where a user might abort the process mid-stream. * **Live Validation Timing**: Validation only starts after the user first attempts to submit (hits Enter). This prevents annoying "field is required" errors while the user is still in the middle of typing their initial response. * **Non-Laravel Projects**: You can use this package in any PHP project. Simply run `composer require laravel/prompts` to bring these interactive features to any CLI tool.
Jul 27, 2023Overview Building full-stack web applications requires a delicate balance between powerful backend logic and snappy, interactive frontends. Laravel continues to dominate this space by prioritizing developer experience and productivity. The latest advancements—ranging from a radical rethinking of the framework skeleton in **Laravel 11** to the introduction of single-file functional components in **Laravel Volt**—aim to remove the traditional friction of web development. This guide explores how these new tools streamline the path from a blank terminal to a high-performance production application. Prerequisites To get the most out of these new features, you should have a firm grasp of the following: * **PHP 8.2+**: Many of the new features, especially in Laravel 11, rely on modern PHP type-hinting and syntax. * **Blade Templating**: Understanding the basics of Blade components and directives. * **Eloquent ORM**: Familiarity with how Laravel handles database models and relationships. * **Command Line Interface (CLI)**: Comfort using Artisan commands for scaffolding and migrations. Key Libraries & Tools * Laravel Herd: A zero-dependency, lightning-fast PHP development environment for macOS that bypasses Homebrew. * Laravel Folio: A page-based routing system that eliminates the need for manual route definitions in `web.php`. * Laravel Volt: An add-on for Livewire 3 that allows for single-file, functional PHP/Blade components. * Laravel 11: The upcoming major version of the framework featuring a minimalist application skeleton. The Laravel 11 Skeleton: Minimalist by Design Laravel 11 introduces a streamlined directory structure that removes the "bloat" often associated with enterprise-grade frameworks. The goal is a light, lean, and modern starting point where you only interact with the files you actually need. The Vanishing Middleware and Kernel One of the most jarring changes is the removal of the `app/Http/Middleware` directory and the `Kernel.php` files. In previous versions, the framework shipped with nine default middlewares. In Laravel 11, these are moved into the framework core, though they remain fully configurable. Instead of a kernel, you now configure your application middleware and routing within the `bootstrap/app.php` file using a fluent, functional interface: ```php ->withMiddleware(function (Middleware $middleware) { $middleware->web(append: [ \App\Http\Middleware\LaraconMiddleware::class, ]); }) ``` Models and Method-Based Casts Eloquents models are also seeing a syntax upgrade. While property-based casting (the `$casts` protected property) still works, Laravel 11 promotes method-based casts. This allows you to call methods directly within the cast definition, providing better IDE support and more expressive code. ```php protected function casts(): array { return [ 'options' => AsEnumCollection::of(UserOption::class), ]; } ``` Page-Based Routing with Laravel Folio Laravel Folio provides an alternative to the traditional routing system. Inspired by frameworks like Next.js, it uses the file system to determine your application's routes. If a file exists at `resources/views/pages/about.blade.php`, it is automatically accessible at `/about`. Dynamic Segments and Route Model Binding Folio handles dynamic parameters using a bracket syntax in the filename. To bind a route to a specific User model, you would name your file `[User].blade.php`. Folio automatically detects this and performs route model binding behind the scenes. ```php // resources/views/pages/users/[User].blade.php <div> <h1>User Profile</h1> <p>Name: {{ $user->name }}</p> </div> ``` For more complex needs, Folio supports **middleware** declaration directly inside the Blade file. By using a `<?php` block at the top of the template, you can assign middleware like `auth` or `can` without ever touching a separate routes file. Single-File Interactivity with Laravel Volt Laravel Volt is perhaps the most significant shift in how Livewire components are written. It allows for "code co-location," where the logic of a component (PHP) lives in the same file as the markup (Blade). The Functional API Volt utilizes a functional API to define state, actions, and validation. Here is a look at a simple counter component written entirely within a single `.blade.php` file: ```php <?php use function Laravel\Volt\{state}; state(['count' => 0]); $increment = fn () => $this->count++; ?> <div> <h1>{{ $count }}</h1> <button wire:click="increment">+</button> </div> ``` This approach eliminates the context-switching required when bouncing between a `Component.php` class and a `component.blade.php` template. It supports the full Livewire feature set, including lifecycle hooks, validation rules, and even URL query string synchronization. Anonymous Components One of Volt's most powerful features is the `volt` directive. It allows you to turn a fragment of a standard Blade page into a reactive Livewire component without creating a separate file in the `Livewire` directory. This is ideal for small pieces of interactivity, such as a live search box or a polling notification list. Syntax Notes * **Trailing Closures**: Many new Laravel APIs utilize trailing closures in their configuration files to keep the syntax clean. * **Wildcard Directories**: In Laravel Folio, use `[...]` for multi-segment parameters and `[Model:slug]` for scoped child binding. * **State Management**: In Laravel Volt, the `state()` function accepts an associative array to initialize component properties. Practical Examples Implementing a Live Search with Volt By combining the `state` and `provide` functions, you can create a high-performance search interface. Using the `->url()` method on the state ensures that the search term stays in the browser's address bar, allowing users to share specific search results. ```php <?php use App\Models\User; use function Laravel\Volt\{state, provide}; state(['search' => ''])->url(); provide(['users' => fn () => User::where('name', 'like', "%{$this->search}%")->get() ]); ?> <div> <input wire:model.live="search" type="text" placeholder="Search users..."> <ul> @foreach($users as $user) <li>{{ $user->name }}</li> @endforeach </ul> </div> ``` Tips & Gotchas * **PHP Version Matters**: Laravel Herd makes switching PHP versions trivial, but ensure your production environment matches the version you used during development to avoid type-hinting errors. * **Folio vs. Traditional Routes**: You don't have to choose one. You can use Laravel Folio for simple marketing pages and traditional routes/controllers for complex business logic. * **Validation in Volt**: Always call `$this->validate()` inside your action closures if you have defined `rules()` at the top of your Volt component. * **Skeleton Upgrades**: Upgrading to Laravel 11 does not require you to adopt the new minimalist skeleton. Your existing Laravel 10 structure will work perfectly fine after the upgrade.
Jul 24, 2023