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

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

3 min read Mohamed Said Mohamed Said

What Actually Changed in Laravel 12

Laravel 12 is a focused release. Rather than introducing sweeping new subsystems, it tightens existing APIs, removes long-deprecated paths, and ships opinionated starter kits that reflect how the community actually builds apps today. If you are running Laravel 11 on PHP 8.2+, the upgrade is low-friction — but a handful of changes will bite you if you skip the release notes.


Starter Kit Overhaul

The most visible change is the replacement of the old Breeze and Jetstream scaffolding with a unified laravel new experience. The new stacks are:

  • React + Inertia (TypeScript-first)
  • Vue + Inertia (TypeScript-first)
  • Livewire (Volt or class-based)
  • API-only (no frontend)

The installer now asks which stack you want and wires everything — Vite config, auth scaffolding, and Pest — in one pass. There is no separate breeze:install step.

laravel new my-app
# Prompts: stack, testing framework, git init

Existing projects are unaffected; the starter kits are only relevant at project creation time.


Application Bootstrap Cleanup

Laravel 12 removes the Http/Kernel.php and Console/Kernel.php files that were already deprecated in Laravel 11. If you upgraded from Laravel 10 and kept those files, they will now be ignored entirely — but any customisation inside them will silently disappear.

Action required: Audit your kernels before upgrading. Move any custom middleware registration to bootstrap/app.php:

// bootstrap/app.php
return Application::configure(basePath: dirname(__DIR__))
    ->withRouting(
        web: __DIR__.'/../routes/web.php',
        api: __DIR__.'/../routes/api.php',
    )
    ->withMiddleware(function (Middleware $middleware) {
        $middleware->append(\App\Http\Middleware\EnforceJsonResponse::class);
    })
    ->withExceptions(function (Exceptions $exceptions) {
        $exceptions->render(fn (\App\Exceptions\DomainException $e, Request $r) =>
            response()->json(['error' => $e->getMessage()], 422)
        );
    })
    ->create();

Typed Route Model Binding Improvements

Laravel 12 makes implicit route model binding respect PHP 8.x union types and intersection types on controller method signatures. Previously, a union-typed parameter would fall back to the default resolver. Now the container inspects the concrete type and resolves accordingly.

// Before Laravel 12: union type caused silent fallback
public function show(Post|Page $resource): Response
{
    // $resource was always the raw route segment string
}

// Laravel 12: resolves the first bindable type found in the union
public function show(Post|Page $resource): Response
{
    // $resource is a hydrated Eloquent model
}

This is opt-in via the Illuminate\Routing\Contracts\BindingRegistrar interface on your model. If neither type implements it, the old behaviour is preserved.


Removed Deprecations Worth Noting

| Removed | Replacement | |---|---| | Route::prefix() chained on group closures without array | Pass options array directly | | $this->middleware() in controllers (constructor style) | Use #[Middleware] attribute | | Str::replaceArray() | Str::replaceMatches() or native str_replace() | | assertExactJson strict key ordering | assertExactJsonStructure |


Minimum PHP Version

Laravel 12 requires PHP 8.2. PHP 8.1 is dropped. If you are still on 8.1, upgrade PHP first — the language changes (readonly classes, never return type, DNF types) are worth it independently.


Upgrade Checklist

  • Run composer require laravel/framework:^12.0 and fix any immediate constraint conflicts.
  • Delete app/Http/Kernel.php and app/Console/Kernel.php after migrating their contents.
  • Search for Str::replaceArray and $this->middleware( in controllers.
  • Run php artisan route:list and confirm no routes silently broke.
  • Execute your full Pest suite — binding changes can surface as 404s in feature tests.

Key Takeaways

  • The kernel files are gone; bootstrap/app.php is the single configuration surface.
  • Starter kits are rebuilt from scratch — existing projects are unaffected.
  • Union-typed route model binding now works correctly without a workaround.
  • PHP 8.2 is the floor; plan your server upgrade before touching composer.json.
  • The removed deprecations are small but silent — a thorough test suite is your safety net.

Found this useful?

Frequently Asked Questions

3 questions
Q01 Do I need to rebuild my existing Laravel 11 app to use the new starter kits in Laravel 12?
No. The new starter kits only apply when you create a fresh project with `laravel new`. Upgrading an existing app to Laravel 12 does not touch your frontend scaffolding.
Q02 What happens if I upgrade to Laravel 12 but still have Http/Kernel.php in my project?
The file is silently ignored. Any custom middleware or exception handling registered there will stop working. You must migrate that logic to bootstrap/app.php before upgrading.
Q03 Is the union-type route model binding change backwards-compatible?
Yes, with a caveat. If neither type in the union implements the binding contract, the old behaviour is preserved. The change only activates when at least one type is a bindable Eloquent model, so existing routes are safe.

Continue reading

More Articles

View all