What to expect in the next generation of Laravel Forge. Read the blog post
Skip to content

Changelog

New updates and improvements to Laravel’s open source products.

August 2025

Laravel Framework 12.x

Add withHeartbeat Method to LazyCollection

Pull request by @JosephSilber

1$collection
2 ->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 visit
20router.visit('/users', {
21 invalidate: ['user']
22})
23 
24// Flush via useForm helper
25form.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 Data
3</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.slug
6 });
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 --verbose
2// App\Jobs\UrgentAction 85 high
3// 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'); // true
2str('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

VS Code Performance Improvements

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