New updates and improvements to Laravel's open source products.
Pull request by @matthewnessworthy
Bursty workloads finally have a clean answer. Debounceable jobs keep only the last dispatch in a window, so a user editing the same document ten times in thirty seconds triggers a single rebuild instead of ten. Apply the #[DebounceFor] attribute to any queued job, or debounce at the call site without touching the class:
1use Illuminate\Contracts\Queue\ShouldQueue; 2use Illuminate\Queue\Attributes\DebounceFor; 3 4#[DebounceFor(30)] 5class RebuildSearchIndex implements ShouldQueue 6{ 7 public function __construct(public int $documentId) {} 8 9 public function debounceId(): string10 {11 return (string) $this->documentId;12 }13 14 public function handle(): void15 {16 SearchIndex::rebuild($this->documentId);17 }18}
Where ShouldBeUnique keeps the first dispatch and rejects the rest, debouncing flips that around so the most recent dispatch always wins. A JobDebounced event fires for any superseded job if you want to track it. Read more in the queue documentation.
Pull request by @WendellAdriel
API-only apps can stop scraping HTML from /up. The built-in health route now returns JSON when the request asks for it, which makes load balancers, uptime monitors, and orchestrators a lot happier:
1{ "status": "Application is up" }
Status codes stay the same, and browser requests still get the existing Blade page. No config required.
JsonFormatterPull request by @cosmastech
Exception context is one of those things you only realize you needed after staring at production logs for hours. The new Illuminate\Log\Formatters\JsonFormatter ensures context() data on your custom exceptions actually shows up in structured logs, including context from previous exceptions in a chain. Wire it up in config/logging.php:
1'formatter' => Illuminate\Log\Formatters\JsonFormatter::class,
Now an exception like this carries its enriched context all the way through to the log entry:
1class MyException extends \RuntimeException 2{ 3 public function context(): array 4 { 5 return [ 6 'luke' => 'cosmastech', 7 'taylor' => 'taylorotwell', 8 ]; 9 }10}
Whether the exception is sent through report() or written via Log::error() with an exception key, the structured payload now includes everything you put on the exception itself.
prefersJsonResponses() to the Application BuilderPull request by @WendellAdriel
Building an API and tired of curl requests coming back as HTML? One line in bootstrap/app.php now treats broad Accept headers as JSON, so auth redirects, validation errors, and exception pages all serialize properly for clients that didn't ask for HTML:
1return Application::configure(basePath: dirname(__DIR__))2 ->withRouting(3 web: __DIR__.'/../routes/web.php',4 commands: __DIR__.'/../routes/console.php',5 )6 ->prefersJsonResponses()7 ->create();
Clients that explicitly request text/html are left alone, and the original Accept value is preserved on X-Original-Accept if you need it for logging. Default behavior is unchanged unless you opt in.
Pull request by @dwightwatson
Cloudflare's Email Service joins the list of first-class mailers in Laravel. Drop your credentials into config/services.php:
1'cloudflare' => [2 'account_id' => env('CLOUDFLARE_ACCOUNT_ID'),3 'token' => env('CLOUDFLARE_TOKEN'),4],
Then point your mailer at it and send like usual. Same Symfony HTTP Client setup the existing transports use, so there's nothing new to learn.
Pull request by @timmylindh
If you've ever fought a CROSSSLOT error on AWS ElastiCache Serverless or any Redis Cluster setup, this one is for you. Laravel's Redis queue and ConcurrencyLimiter now wrap queue names in hash tags automatically when they detect a cluster connection, so all related keys land on the same slot:
1queues:{default} -> slot for "default"2queues:{default}:delayed -> slot for "default"3queues:{default}:reserved -> slot for "default"4queues:{default}:notify -> slot for "default"
Different queues still distribute across the cluster, the same pattern Sidekiq and BullMQ use. The PhpRedis cluster connector also picks up ACL auth and retry/backoff parity with standalone connections, which matters during ElastiCache scaling events. Standalone users see zero change. The full breakdown is in the queue documentation.
Pull request by @jackbayliss
Three new methods on the Queue facade let you peek at what is actually sitting on your queues without reaching for DB::table() and decoding payloads by hand:
1Queue::pendingJobs();2Queue::delayedJobs();3Queue::reservedJobs();
Each returns a collection of InspectedJob instances exposing the uuid, job name, attempts, and creation time. That opens up some genuinely useful patterns:
1Queue::reservedJobs('high-priority-queue')->first()->name;2// => 'App\Jobs\SendEmail'3 4Queue::pendingJobs()->countBy('name');5// => ['App\Jobs\SendEmail' => 1842, 'App\Jobs\ProcessOrder' => 43]
Perfect for deployment scripts that need to wait for specific jobs to finish, dashboards, and ad-hoc debugging. Works with the Redis and Database drivers; other drivers return an empty collection.
Pull request by @NurullahDemirel
Strict mode lands in form requests, mirroring the spirit of Model::shouldBeStrict(). Flip it on in your AppServiceProvider and any incoming field that isn't declared in rules() will fail validation instead of silently slipping through:
1use Illuminate\Foundation\Http\FormRequest;2 3public function boot(): void4{5 FormRequest::failOnUnknownFields(! app()->isProduction());6}
Need to opt a single request out (say, a public webhook with unpredictable payloads)? Set the property on the class:
1class PublicWebhookRequest extends FormRequest2{3 protected ?bool $failOnUnknownFields = false;4 5 public function rules(): array { /* ... */ }6}
It's fully backward compatible, off by default, and a great safety net during development. Learn more in the validation documentation.
onHttpException, onNetworkError, And Response to onSuccess in useHttpPull request by @pascalbaljet
The useHttp hook gets the same error callbacks Inertia visits already have. No more wrapping calls in try/catch just to handle a 500 or a dropped connection. There's also a second argument on onSuccess that hands you the raw HttpResponse, so telling a 200 apart from a 204 is now trivial. Available across the React, Vue, and Svelte adapters.
Pull request by @pushpak1300
Configuring Laravel Nightwatch is now a built-in skill for both Claude Code and Cursor. Install the plugin from the marketplace and your agent has the steps it needs to wire Nightwatch into your project, no copy-pasting setup instructions from the docs.
Pull request by @pushpak1300
The Boost installer now offers Laravel Cloud as one of the integrations you can pull in, which means the deploying-laravel-cloud skill ships alongside your other agent tooling. One less manual step on the way to your first deploy. Read more about getting started at Laravel Cloud.
Pull request by @me-shaon
Wondering what skills are actually available in your project? A new Artisan command shows you:
1php artisan boost:skill-list
You get a clean table of skill names alongside their source (the package they came from, or local if it's yours), with custom skills called out so they're easy to spot. Pass --json for machine-readable output if you're scripting around it:
1[ 2 { 3 "name": "livewire-development", 4 "description": "Build dynamic interfaces using Livewire", 5 "package": "livewire/livewire", 6 "custom": false 7 }, 8 { 9 "name": "my-custom-skill",10 "description": "A custom skill for my project",11 "package": "user",12 "custom": true13 }14]
Pull request by @oniice
Kiro, Amazon's AI-powered IDE, is now a first-class agent target alongside Cursor, Claude Code, and Copilot. Boost detects Kiro automatically and writes guidelines, skills, and MCP config to the right places (.kiro/steering/boost.md, .kiro/skills/, and .kiro/settings/mcp.json). Whatever editor you live in, your Laravel context comes with you.
clickOnceEnabled() and clickOnceVisible()Pull request by @weshooper
Two small additions, big quality-of-life win. The new clickOnceEnabled() and clickOnceVisible() browser methods wait for an element to be ready before clicking it exactly once, no pause() calls, no flaky retries:
1$browser->clickOnceVisible('@save-button');2$browser->clickOnceEnabled('@submit-button');
A clean way to keep your test suite stable on slower pages.
Pull request by @joetannenbaum
Svelte 5 developers can now use Laravel Echo with the same ergonomics React and Vue have enjoyed. The new useEcho rune lets you subscribe to channels and react to events with full reactivity:
1<script>2 import { useEcho } from '@laravel/echo-svelte';3 4 const { channel } = useEcho('orders', 'OrderShipped', (e) => {5 console.log(e.order);6 });7</script>
Real-time features in Svelte are now a single import away.
Pull request by @timmylindh
A companion to the framework's queue cluster work, Horizon now runs cleanly on AWS ElastiCache Serverless and other Redis Cluster deployments. The Horizon prefix is automatically wrapped in a hash tag so all keys land on the same slot, and Horizon::use() registers cluster connections properly instead of extracting a single node. Standalone setups behave exactly as before, so no migration is needed if you're not on a cluster. Full setup guide in the Horizon documentation.
Pull request by @joetannenbaum
laravel new is a much calmer experience now. Instead of a wall of composer, npm, and artisan output, each phase (project create, key generate, migrations, frontend install, Pest, Boost, git init, GitHub push) renders as its own task with clear pass or fail indicators thanks to Laravel Prompts' task() helper. Want the full firehose? Pass -v and the original streaming output is right there.
Pull request by @nunomaduro
PAO ships out of the box with every starter kit (Inertia, Livewire, blank, WorkOS variants and all). New apps come ready for AI-friendly tooling output with no extra setup.
Pull request by @WendellAdriel
Generic "Saved" messages are out, proper toast notifications are in. All 21 starter kit variants now use specific, translatable messages like "Profile updated." or "Team created." Livewire kits use Flux's native Flux::toast, while Inertia kits use Sonner with a tidy Inertia::flash pattern that's identical across React, Svelte, and Vue. Polished feedback, consistent everywhere.
Pull request by @pushpak1300
MCP tools were text-only until now. With MCP UI App support, a tool can render a self-contained HTML app inside a sandboxed iframe in the host. Scaffold one with a single command:
1php artisan make:mcp-app-resource DashboardApp
The default handle() auto-infers the Blade view from the class name, so the PHP side stays minimal:
1class DashboardApp extends AppResource2{3 public function handle(Request $request): Response4 {5 return Response::view('mcp.dashboard-app', [6 'title' => $this->title(),7 ]);8 }9}
The view is the entire app, HTML, JS, and CSS in one file with the bundled SDK already inlined:
1<x-mcp::app title="Dashboard"> 2 <x-slot:head> 3 <script type="module"> 4 createMcpApp(async (app) => { 5 const result = await app.callTool('get-data'); 6 document.getElementById('output').textContent = result.content[0]?.text; 7 }); 8 </script> 9 </x-slot>10 <div id="output"></div>11</x-mcp::app>
Link a tool to the app with the #[RendersApp] attribute, optionally hiding helper tools from the model so only the UI can call them. No npm, no Vite, just Blade.
Laravel now ships a full passkey story instead of asking you to assemble WebAuthn by hand.
laravel/passkeys-server is published on Packagist as laravel/passkeys. It brings migrations, routes for login, confirmation, and credential management, plus WebAuthn actions, events, and escape hatches when you need custom authorization, responses, or your own route definitions.
@laravel/passkeys (source) handles browser ceremonies—registration and verification—with a small core API and first-class helpers for React, Vue, and Svelte, including SSR-safe hooks so client-only APIs do not fight your framework.
Laravel Fortify integrates the stack behind Features::passkeys() and a passkeys section in config/fortify.php, so Fortify apps get the same endpoints and contracts (PasskeyUser, PasskeyAuthenticatable) without reimplementing glue.
Together: server package, npm client, and Fortify line up on routes and contracts so passwordless auth stays boring to wire up and portable across stacks.
Pull request by @nunomaduro
When agents run Pint alongside other tooling, consistent output matters. Pint's agent reporter now mirrors the PAO format, adding a tool key and using passed instead of pass:
1{"tool":"pint","result":"passed"}2{"tool":"phpstan","result":"passed","errors":0}3{"tool":"pest","result":"passed","tests":52,"passed":52,"assertions":90,"duration_ms":534}
Small change, much easier to parse in agent workflows.
Pull request by @N1ebieski
The VS Code extension now understands Laravel 13's new attributes. You get autocompletion, hover tooltips, navigation, and diagnostics for routing attributes like Authorize, Middleware, and RedirectToRoute, plus model attributes like Fillable, Guarded, Hidden, Visible, and Appends. Writing controllers and Eloquent models with attributes feels just as smooth as the array syntax did.
Pull request by @TitasGailius
Thirty common Artisan commands are now a keystroke away in the VS Code command palette, including migrate, migrate:fresh, tinker, db, route:list, pail, queue:retry, schedule:list, cache:clear, and more. Each runs in a dedicated Laravel Artisan terminal so you can read the output directly. Less context switching, more shipping.
Pull request by @TitasGailius
Pest test files get a real intellisense upgrade. The extension now reads your Pest config to generate helper docblocks that preserve the right $this type inside hooks and tests, surface pest()->extend(...) and pest()->use(...) setups for autocomplete and navigation, and expose any custom expectations you have defined. Generated docblocks live in storage/framework/testing/_pest.php, which is gitignored. Writing Pest tests in VS Code feels noticeably better.
Pull request by @TitasGailius
A new Laravel: Go to route command opens a fuzzy picker of every registered route, by path or name, and jumps straight to the handler. No more grepping for a controller method or scanning route:list. One of those small commands you wonder how you lived without after a week.