Changelog
New updates and improvements to Laravel’s open source products.
September 2025
Laravel Framework 12.x
[12.x] Introduce after
Rate Limiting
Pull request by @timacdonald
1use Illuminate\Http\Request; 2use Illuminate\Support\Facades\RateLimiter; 3use Symfony\Component\HttpFoundation\Response; 4 5/* 6 * Ensure a user can only hit ten 404 responses in a minute before they are 7 * rate limited to ensure user's cannot enumerate resource IDs. 8 */ 9RateLimiter::for('resource-not-found', function (Request $request) {10 return Limit::perMinute(10)11 ->by("user:{$request->user()->id}")12 13 // The new `after` hook...14 ->after(function (Response $response) {15 return $response->getStatusCode() === 404;16 });17});
The new RateLimiter::after
hook lets you apply rate limiting based on the response as well as the request.
[12.x] Add Http::batch
Pull request by @WendellAdriel
1$responses = Http::batch(fn (Batch $batch) => [ 2 $batch->get('http://localhost/first'), 3 $batch->get('http://localhost/second'), 4 $batch->get('http://localhost/third'), 5])->before(function (Batch $batch) { 6 // This runs before the first HTTP request is executed. 7})->progress(function (Batch $batch, int|string $key, Response $response) { 8 // This runs after each successful HTTP request from the Batch. 9})->catch(function (Batch $batch, int|string $key, Response|RequestException $response) {10 // This runs after each failed HTTP request from the Batch.11})->then(function (Batch $batch, array $results) {12 // This runs ONLY IF all the HTTP requests from the Batch are successful and the batch is not cancelled.13})->finally(function (Batch $batch, array $results) {14 // This runs after all the HTTP requests from the Batch finish and the batch is not cancelled.15})->send();
The new Http::batch
method allows you to hook into the lifecycle of each HTTP call when making multiple requests in parallel.
[12.x] Adds Macroable
Trait to Illuminate/Support/Benchmark
Pull request by @1tim22
1use Closure;2use Illuminate\Support\Benchmark;3use Illuminate\Support\Facades\Log;4 5Benchmark::macro('log', fn (Closure $callback) =>6 Log::debug(Benchmark::measure($callback))7);8 9Benchmark::log(fn () => sleep(1));
Benchmark
is now updated with the Macroable
trait to allow arbitrary macro registration.
[12.x] Add --whisper
Option to schedule:work
Command
Pull request by @thojo0
You can now pass a --whisper
option to the schedule:work
command. The option is passed through to the schedule:run
command, which hides the "No scheduled commands are ready to run" message from the output, producing cleaner logs and terminal output and reducing noise for monitoring tools.
[12.x]: Cache Session Driver
Pull request by @joaopalopes24
1$discount = $request->session()->cache()->get('discount');2 3$request->session()->cache()->put(4 'discount', 10, now()->addMinutes(5)5);
The new session cache provides a convenient way to cache data that is scoped to an individual user session. Unlike the global application cache, session cache data is automatically isolated per session and is cleaned up when the session expires or is destroyed.
[12.x] Add Support for #[UseResource(...)] and #[UseResourceCollection(...)] Attributes on Models
Pull request by @Lukasss93
1use App\Http\Resources\MyCustomNameResource; 2use App\Http\Resources\MyCustomNameCollectionResource; 3 4#[UseResource(MyCustomNameResource::class)] 5#[UseResourceCollection(MyCustomNameCollectionResource::class)] 6class MyModel extends Model { 7 // 8} 9 10// Now you can call:11$model->toResource();12$collection->toResourceCollection();
You can now define resources classes directly on your Eloquent models using PHP attributes.
[12.x] Add --json
Option to schedule:list
Pull request by @dxnter
1[ 2 { 3 "expression": "0 0 15 * *", 4 "repeat_seconds": null, 5 "command": "php artisan backup:run", 6 "description": "Run daily backup process", 7 "next_due_date": "2025-09-15 00:00:00 +00:00", 8 "next_due_date_human": "5 days from now", 9 "timezone": "UTC",10 "has_mutex": false11 }12]
The schedule:list
command now accepts a --json
flag to allow programmatic consumption of scheduled task data for monitoring and integration purposes.
[12.x] Add Prepend Option for Str::plural()
Pull request by @caseydwyer
1{{-- Instead of: --}}2We had {{ number_format($attendees->count()) . ' ' . Str::plural('attendee', $attendees->count()) }} at Laracon 2025.3 4{{-- Now we can: --}}5We had {{ Str::plural('attendee', $attendees->count(), prependCount: true) }} at Laracon 2025.
The Str::plural
helper now accepts a new argument, prependCount
, that will prepend the count to the string automatically.
[12.x] Add Support for SQS Fair Queue
Pull request by @crynobone
SQS fair queues are now supported.
[12.x] Update Local Exception Page
Pull request by @avosalmon

The exception page was given a refresh, making local errors clearer and more actionable.
NPM Trusted Publishing
This month, we switched all of our NPM packages over to Trusted Publishing. Now you'll know where the latest release originated and you can verify that it came from us.

Inertia
Introduction of the <InfiniteScroll>
Component
Pull request by @pascalbaljet
1<template>2 <InfiniteScroll data="users">3 <div v-for="user in users.data" :key="user.id">{{ user.name }}</div>4 </InfiniteScroll>5</template>
A new <InfiniteScroll>
component was introduced for React, Vue, and Svelte that allows you to easily create infinite scroll interfaces by wrapping your items in the new component.
Support for Merging Nested Prop Paths
Pull request by @pascalbaljet
A new middle ground was added between Inertia::merge()
and Inertia::deepMerge()
so you can have fine-grained control over what should be merged.
The front-end will replace the entire object, except the data
array. The new items will be appended to the existing items:
1Inertia::merge(User::paginate())->append('data');
There's also prepend()
, and you can also combine multiple calls:
1Inertia::merge($complexObject)2 ->append('users', matchOn: 'username')3 ->prepend('messages.data');
Then there's a new Inertia::scroll()
function which you can pass paginated resources:
1Inertia::scroll(User::paginate());
Progress Indicator API
Pull request by @pascalbaljet
1import { progress } from "@inertiajs/vue3"; 2 3progress.start(); // Begin progress animation 4progress.set(0.25); // Set to 25% complete 5progress.finish(); // Complete and fade out 6progress.reset(); // Reset to start 7progress.remove(); // Complete and remove from DOM 8progress.hide(); // Hide progress bar 9progress.reveal(); // Show progress bar10 11progress.isStarted(); // Returns boolean12progress.getStatus(); // Returns current percentage or null
The internal methods used to drive the Progress indicator are now exposed publicly, giving developers control over the progress bar.
Boost
[1.x] Adds boost:update
+ Allows Package Authors to Publish Guidelines
Pull request by @nunomaduro
Boost can now self-update with the new boost:update
command. In addition, third-party packages can now auto-register their own guidelines by including a resources/boost/guidelines/core.blade.php
file in their package.
Allow Guideline Overriding
Pull request by @ashleyhindle
Users can now override guideline files, specifying that Boost should use their guidelines in specific instances instead of the default guidelines that come with Boost.
Echo
Add stopListeningForNotification
Pull request by @thoresuenert
1const callback = (notification) => { 2 console.log(notification.type); 3}; 4 5// Start listening... 6Echo.private(`App.Models.User.${userId}`).notification(callback); 7 8// Stop listening (callback must be the same)... 9Echo.private(`App.Models.User.${userId}`).stopListeningForNotification(10 callback11);
A new stopListeningForNotification
method was added to stop listening for notifications without leaving the channel.
Fortify
Regenerate Session on Register
Pull request by @valorin
The session and session and CSRF token are now regenerated on user registration.
Installer
Check for New Version of Installer on new
Pull request by @joetannenbaum
The installer now checks if there are updates available on Packagist to ensure you are using the latest version when running laravel new
.
Support PNPM and Yarn Package Managers
Pull request by @adrum
The installer now detects lock files for the PNPM and Yarn package managers, falling back to NPM if neither are found.
Add the Ability to Use a Git Repo as Custom Kit
Pull request by @adrum
1laravel new --using https://gitub.com/user/project
You can specify a git repo as a custom starter kit when running laravel new
.
MCP
First Official "Beta" Release of Laravel MCP Package
https://github.com/laravel/mcp
Laravel MCP allows you to rapidly build MCP servers for your Laravel applications. MCP servers allow AI clients to interact with your Laravel application through the Model Context Protocol.
Prompts
Improve Display of Progress Bars With High Step Counts
Pull request by @jessarcher

The display of the progress bar when using a large number of steps is improved by adding thousands separators and extra spacing.
Reverb
[1.x] Adds Optional Application Level Connection Limits
Pull request by @joedixon
You can specify connection limits for those users who wish to limit the number of active connections to a given application as supported by the Pusher protocol.
VS Code Extension
Add Commands That Refactor Class Attributes in Blade Files
Pull request by @N1ebieski

Commands are now available that refactor the selected or all class attributes to the @class
directive.
Add a Command That Wraps Selected Text With a Helper
Pull request by @N1ebieski

Commands are now available that wrap the selected text with a helper, such as dd
, collect
, dump
, and str
.
Pint Commands + Run Pint on Save
Pull request by @joetannenbaum
Commands are now available to "Run Pint", "Run Pint on Dirty Files", and "Run Pint on Current File". There is also now an optiont to run Pint on save for the current file.
August 2025
Laravel Framework 12.x
Add withHeartbeat
Method to LazyCollection
Pull request by @JosephSilber
1$collection2 ->withHeartbeat(CarbonInterval::minutes(5), function () {3 // ...4 })5 ->each(function ($item) {6 // ...7 });
The new withHeartbeart
method allows you to run a callback at regular time intervals while the collection is being lazily enumerated, particularly useful for long-running processes.
Add toPrettyJson
Method
Pull request by @WendellAdriel
1$collection = collect([1,2,3]);2 3// Instead of this:4$collection->toJson(JSON_PRETTY_PRINT);5 6// You can now do this:7$collection->toPrettyJson();
Add allowedUrls
Through preventStrayRequests
Pull request by @rabrowne85
1Http::allowStrayRequests([2 'http://127.0.0.1:13714/*',3]);
The Http::allowStrayRequests
method now accepts an argument that allows you to specify URLs that tests are allowed to send requests to when using Http::preventStrayRequests
.
Add "Copy as Markdown" Button to Error Page
Pull request by @mpociot
There is now a "Copy as Markdown" button on the Laravel exception page. When clicking this button, a markdown representation of the exception is copied to the users clipboard, which can then be used for AI agents/LLMs.
Add New mergeVisible
, mergeHidden
and mergeAppends
Methods
Pull request by @jonerickson
1protected function initializeTwoFactorAuthentication(): void 2{ 3 $this->mergeHidden([ 4 'app_authentication_secret', 5 'app_authentication_recovery_codes', 6 ]); 7 8 $this->mergeCasts([ 9 'app_authentication_secret' => 'encrypted',10 'app_authentication_recovery_codes' => 'encrypted:array',11 ]);12}
Additional attribute helper methods added to Eloquent models for merging visibility-related arrays, bringing them in line with existing helpers like mergeCasts
, mergeFillable
, and mergeGuarded
.
Add Arr::push()
Pull request by @inxilpro
1if ($this->hasChanges($data)) {2 Arr::push($result, 'pending.changes', $data);3}
A new Arr
method that allows you to push something to an array if the array exists, or initialize it to an empty array and then push to it if it doesn't.
Inertia
Introduction of the Form
Component
Pull request by @pascalbaljet
1<script setup> 2import { Form } from "@inertiajs/vue3"; 3</script> 4 5<template> 6 <Form action="/users" method="post"> 7 <input type="text" name="name" /> 8 <input type="email" name="email" /> 9 <button type="submit">Create User</button>10 </Form>11</template>
A new <Form>
component that behaves much like a classic HTML form and simply allows Inertia to intercept the network call on form submission. This new component greatly reduces the boilerplate needed to create a form with Inertia.
Support for Passing Wayfinder Objects to Router Methods
Pull request by @pascalbaljet
1import UserController from "@/actions/App/Http/Controllers/UserController"; 2 3// Regular visits 4router.visit(UserController.store()); 5 6// Prefetching 7router.prefetch(UserController.index()); 8router.getPrefetching(UserController.index()); 9router.getCached(UserController.index());10router.flush(UserController.index());
Adds support for passing Wayfinder-like objects into router methods instead of specifying the URL and method manually.
Tag-Based Cache Invalidation for Prefetch Requests
Pull request by @pascalbaljet
1// Specify cache tags via the Link component 2<Link href="/users/1" prefetch="hover" :tags="['user', 'profile']"> 3 View Profile 4</Link> 5 6// Or via prefetch method on the route 7router.prefetch( 8 '/users', 9 { method: 'get', data: { page: 2 } },10 { cacheFor: '1m', tags: ['users'] }11)12 13// Flushes all cache entries tagged with 'user'14router.flushByTags(['user'])15 16// Flushes entries tagged with 'user' OR 'product'17router.flushByTags(['user', 'product'])18 19// Flush via visit20router.visit('/users', {21 invalidate: ['user']22})23 24// Flush via useForm helper25form.post('/users', { invalidate: ['users'] })
Introduces tag-based cache invalidation for prefetch requests. This enables better control over the prefetch cache without having to flush everything.
Support for Passing Custom Component to as
Prop of Link
Component
Pull request by @pascalbaljet
1<Link as={CustomButton} method="post" href="/user" data={{ test: "data" }}>2 Custom Component with Data3</Link>
The Inertia Link
component now supports custom components instead of just HTML tags such as a
or button
.
Boost
Laravel Boost was released this month! Not only was it just released, it is already aware and up-to-date with the just-released Pest 4 and Filament 4.
Add enabled
Option to Config
Pull request by @xiCO2k
Adds a config('boost.enabled')
option to allows users to specifically enable/disable Boost in certain scenarios outside of environment constraints.
Update Pint Guideline to Use --dirty
Flag
Pull request by @yitzwillroth
Updates Boost guidelines to prefer the usage of Pint with the --dirty
flag for cleaner diffs and PRs.
React + Vue Starter Kits
Migrate useForm
to New Inertia Form
Component
Pull request by @joetannenbaum
1import { Form } from "@inertiajs/react";2 3export default function Login() {4 return (5 <Form action="/login" method="post">6 {/* Form fields */}7 </Form>8 );9}
Reduces the boilerplate for all forms in the starter kits by replacing useForm
with the new Inertia Form
component.
Replace Ziggy With Wayfinder
Pull request by @joetannenbaum
1import AuthenticatedSessionController from "@/actions/App/Http/Controllers/Auth/AuthenticatedSessionController"; 2import { Form } from "@inertiajs/react"; 3 4export default function Login() { 5 return ( 6 <Form {...AuthenticatedSessionController.store.form()}> 7 {/* Form fields */} 8 </Form> 9 );10}
Removes Ziggy from the starter kits and replaces it with the current version of Laravel Wayfinder, bringing end-to-end type safety to your routes.
100% Baseline Test Coverage
Pull request by @JaavierR
Adds the missing feature tests so that fresh projects start with 100% test coverage.
Sail
Allow Sail to Run Pest 4 Browser Tests
Pull request by @rogerio-pereira
Docker configuration updates to allow you to run Pest 4 browser tests in Sail.
VS Code Extension
Add DDEV Support
Pull request by @damms005
Adds proper support for DDEV, Docker-based PHP environments.
Add Support for Custom View Extensions
Pull request by @ryangjchandler
The extension now recognizes custom registered view extensions such as .blade.sh
or .blade.ts
and will now autocomplete and link correctly to these files.
Support for Configs in Subfolders
Pull request by @N1ebieski
The extension now supports configs in nested subfolders (e.g. config/foo/bar/baz.php
), providing better autocomplete and linking.
Link Directly to Problem Files
Pull request by @N1ebieski
Now, when the extension warns you about an invalid value, such as a config key, the warning will directly link you to the file where you can fix the issue.
Autocompletion for Route::is
and routeIs
Methods
Pull request by @N1ebieski
Full autocomplete and linking support for the Route::is
and routeIs
methods.
Wayfinder
Introduce Route Utility Types for Improved DX
Pull request by @istiak-tridip
1import type { RouteDefinition } from "@/wayfinder";2 3const sendRequest = (route: RouteDefinition<"post">) => {4 //5};6 7sendRequest(StorePostController());
Utility types are now exported, making them easier to consume if your app requires them.
Support for Providing Default URL Parameters via the Frontend
Pull request by @owenconti
1setup({ el, App, props }) {2 const root = createRoot(el);3 4 setUrlDefaults({5 workspace: props.initialPage.props.workspace.slug6 });7 8 root.render(<App {...props} />);9},
In instances where Wayfinder is unable to determine the proper default URL params as defined by the server, you can now specify them on the client side using setUrlDefaults
.
July 2025
Laravel Framework 12.x
Added Verbose Output for queue:work
Pull requests by @seriquynh & @amirhshokri
1php artisan queue:work --verbose2// App\Jobs\UrgentAction 85 high3// App\Jobs\NormalAction 84 default
With --verbose
, you’ll now see queue names alongside each job and job ID, making multi-queue debugging straightforward and saving you time when tracing issues.
Uri
Implements JsonSerializable
Pull request by @devajmeireles
1echo json_encode(new Uri('/path?x=1'));2// "http://localhost:8000/path?x=1"
This release fixes a serialization bug, properly converting the URI to a string when serializing a larger JSON object that contains the URI as a value.
Added doesntStartWith()
& doesntEndWith()
Str
Methods
Pull request by @balboacodes
1str('apple')->doesntStartWith('b'); // true2str('apple')->doesntEndWith('e'); // false
The inverse of startsWith
/endsWith
, these new methods allow you to fluently test for starting and ending characters in your string.
Closure Support in pluck()
Pull request by @ralphjsmit
1$users->pluck(fn($u) => $u->email, fn($u) => strtoupper($u->name));
You can now dynamically generate keys and values via callbacks. Instead of mapping then plucking, you get a single, flexible method that reduces steps and keeps intent clear.
Pint
Added --parallel
Option
Pull request by @nunomaduro
1./vendor/bin/pint --parallel
By leveraging PHP CS Fixer’s parallel runner, Pint now formats files concurrently. On large codebases that once took minutes, you’ll see results in seconds, speeding up both local workflows and CI pipelines for a clear quality of life win.
Telescope
Migrated to Vite
Pull request by @nckrtl
Telescope’s frontend now uses Vite instead of Mix. Asset builds finish faster, hot-reloads are more reliable, and you benefit from a modern toolchain without changing your workflow.
Octane
FrankenPHP’s File Watcher
Pull request by @kohenkatz
Octane now relies on FrankenPHP’s native file watcher for reloading the server on file changes. This removes the NodeJS dependency and eases the local development process significantly.
Inertia
Reset Form State and Clear Errors with a Single Method
Pull request by @pascalbaljet
1const form = useForm({ name: "", email: "" });2 3// Instead of:4form.reset();5form.clearErrors();6 7// You can now:8form.resetAndClearErrors();
You can now concisely reset all fields and errors in one go, bringing your form back to square one.
Prevent Minifying JavaScript Builds and Test Apps
Pull request by @pascalbaljet
Inertia no longer distributes minified builds, aligning it with the strategy of other popular libraries. This makes debugging more straightforward and allows for local patching if the need arises.
June 2025
Laravel Framework 12.x
Added encrypt()
and decrypt()
String Helper Methods
Pull request by @KIKOmanasijev
You can now chain encrypt()
and decrypt()
directly on a Str
instance, so instead of piping your string through separate encryption calls, you can write:
1$value = Str::of('secret')->encrypt()->prepend('Encrypted: ');2$original = Str::of($value)->decrypt();
This keeps your string-manipulation chains concise (no need to write separate, extra code to handle encryption) and you don’t have to manually wrap a value in encryption calls each time.
Learn more about encrypt
and decrypt
Added broadcast_if()
and broadcast_unless()
Utilities
Pull request by @taylorotwell
You now have two methods for conditional broadcasting in a single call:
1broadcast_if($order->isPaid(), new OrderShipped($order));2broadcast_unless($user->isActive(), new InactiveUserAlert($user));
This replaces multi-line conditionals around broadcast()
and makes your event logic more readable, improving the overall developer experience.
Read the docs about event broadcasting in Laravel
Added --batched
Flag to make:job
Pull request by @hafezdivandari
The php artisan make:job
command now accepts a --batched
option:
1php artisan make:job ProcessPodcast --batched
This command scaffolds the job with the Batchable
trait already applied, so you don’t have to add it manually. It saves you a manual step and ensures consistency.
Learn more about defining batchable jobs
VS Code Extension

Memory Improvements
Pull request by @joetannenbaum
We fixed a memory-leak issue in the extension’s background processes, reducing its long-term footprint and preventing slowdowns during extended coding sessions.
Improved Startup Time
Pull request by @joetannenbaum
Initialization with the VS Code extension now completes in under one second (down from 5–7 seconds) by deferring heavy setup until it’s actually needed. You’ll notice the extension loads almost instantly so you can start building faster.
Get started with the Laravel VS Code extension
Inertia
Allow deepMerge
on Custom Properties
Pull request by @mpociot
When implementing infinite scrolling with Inertia or other paginated data merges, you can now specify a key (matchOn
) to do a deep merge, matching items by ID and replacing or appending as appropriate. This provides greater flexibility, prevents duplicated entries, and keeps your client-side data consistent.
1Inertia::render('Users/Index', [2 'users' => Inertia::deepMerge($users)->matchOn('data.id'),3]);
Learn more in the Inertia docs
Prevent Duplicate Render in React
Pull request by @pascalbaljet
We fixed a React-specific issue in <StrictMode>
where the initial Inertia page would render twice. Now you get a single, clean first render without flicker, improving perceived performance.
Get started with the Laravel + React Starter Kit