Laravel 12: New Features, Helpers, and Practical Upgrade Notes
#laravel #laravel-12 #upgrade #php

Laravel 12: New Features, Helpers, and Practical Upgrade Notes

4 min read Mohamed Said Mohamed Said

What Actually Changed in Laravel 12

Laravel 12 is a focused release. Rather than introducing sweeping architectural shifts, it refines the developer experience around application bootstrapping, ships opinionated starter kits as first-party packages, and adds a handful of ergonomic helpers. If you are already on Laravel 11, the upgrade surface is small — but a few spots will bite you if you skip the release notes.


Starter Kits as First-Party Packages

The most visible change is that the React, Vue, and Livewire starter kits are now maintained as separate Composer packages under the laravel/ namespace rather than being baked into the installer scaffolding.

# React + Inertia starter kit
composer create-project laravel/laravel my-app
cd my-app
php artisan install:api          # optional API scaffolding
php artisan install:broadcasting # optional Reverb scaffolding

# Then pull the starter kit of your choice
php artisan breeze:install react --typescript

This separation means the core framework no longer ships with any frontend opinion. The laravel/breeze and laravel/jetstream packages are updated to match, but the install commands remain familiar.


Bootstrap and Application Structure

Laravel 11 introduced the slimmed-down bootstrap/app.php that replaced app/Http/Kernel.php and app/Console/Kernel.php. Laravel 12 keeps that structure and makes it the only supported path — the legacy kernel stubs are removed from the installer entirely.

If you are upgrading from Laravel 10 directly, you must migrate to the new bootstrap style before targeting Laravel 12:

// bootstrap/app.php — the only kernel you need
return Application::configure(basePath: dirname(__DIR__))
    ->withRouting(
        web: __DIR__.'/../routes/web.php',
        api: __DIR__.'/../routes/api.php',
        commands: __DIR__.'/../routes/console.php',
        health: '/up',
    )
    ->withMiddleware(function (Middleware $middleware) {
        $middleware->web(append: [
            \App\Http\Middleware\HandleInertiaRequests::class,
        ]);
    })
    ->withExceptions(function (Exceptions $exceptions) {
        //
    })
    ->create();

New and Refined Helpers

Number::spell() and Number::ordinal()

The Number facade gains two helpers that are genuinely useful for UI copy:

use Illuminate\Support\Number;

Number::spell(3);        // 'three'
Number::ordinal(21);     // '21st'
Number::ordinal(2);      // '2nd'

Str::chopStart() and Str::chopEnd()

These trim a specific prefix or suffix exactly once — cleaner than a regex for URL or path manipulation:

use Illuminate\Support\Str;

Str::chopStart('/api/v1/users', '/api'); // '/v1/users'
Str::chopEnd('hello.blade.php', '.php'); // 'hello.blade'

once() Global Helper

The once() helper memoizes the result of a closure for the lifetime of the request, scoped to the object instance when called inside a class:

class PricingService
{
    public function baseRate(): float
    {
        return once(fn () => $this->fetchRateFromDatabase());
    }
}

This is particularly useful in Filament resources where the same computed value is accessed multiple times during a single render cycle.


Upgrade Checklist

  1. Require Laravel 12 — update composer.json to "laravel/framework": "^12.0" and run composer update.
  2. Migrate legacy kernels — if still on app/Http/Kernel.php, follow the Laravel 11 upgrade guide first.
  3. Check removed facadesSchema::connection() shorthand behaviour was tightened; explicit connection calls are now required.
  4. Review middleware aliases — any string-keyed middleware aliases registered in the old kernel must move to bootstrap/app.php via $middleware->alias().
  5. Run your test suite — Pest and PHPUnit both work; no test runner changes are required.
  6. Update laravel/breeze or laravel/jetstream — pull the latest major version of whichever starter kit you use.

Takeaways

  • Laravel 12 is an evolutionary release; the upgrade from 11 is low-risk.
  • Starter kits are now decoupled first-party packages, not installer scaffolding.
  • The slim bootstrap/app.php is now the only supported bootstrap path.
  • Str::chopStart(), Str::chopEnd(), Number::ordinal(), and once() are immediately useful in real applications.
  • Upgrading from Laravel 10 requires an intermediate stop at Laravel 11 to adopt the new bootstrap structure.

Found this useful?

Frequently Asked Questions

3 questions
Q01 Is the upgrade from Laravel 11 to Laravel 12 a large effort?
No. If you are already on Laravel 11 with the slim bootstrap structure, the upgrade is mostly a version bump in composer.json plus reviewing any middleware alias registrations. The main work is for teams still on Laravel 10 who need to adopt the new bootstrap path first.
Q02 Does the `once()` helper persist across requests in Laravel Octane?
No. `once()` is scoped to the current object instance and is reset between requests in Octane because the object itself is typically re-instantiated per request. If you reuse a singleton across requests under Octane, you must clear memoized state manually in the request lifecycle hooks.
Q03 Are the new starter kits compatible with Filament v3 and v4?
The starter kits and Filament are independent. Filament manages its own panel routing and does not depend on Breeze or Jetstream. You can install a starter kit for your public-facing UI and run Filament panels alongside it without conflict.

Continue reading

More Articles

View all