Laravel 12: What's New and What It Means for Your Stack
A practical breakdown of the changes in Laravel 12 — the new starter kits, type-safe config helpers, and first-class Pest integration — and how I've updated my team's workflow to take advantage of them.
Laravel 12 arrived with less fanfare than some of its predecessors, but the changes compound meaningfully once you start working with them day-to-day.
Revamped starter kits
The biggest visible change is the completely rewritten starter kit system. The old scaffolding (Breeze, Jetstream) has been replaced with a unified kit that integrates Livewire and Volt by default. If you’re building a traditional MPA, the developer experience is now noticeably smoother — authentication scaffolding, profile management, and session handling all come pre-wired.
For SPA teams (like mine, where the frontend is Vue), the API-only path is cleaner too. The new --api flag generates a starter without any Blade/Livewire overhead.
laravel new my-project --api
Lean, headless, ready to attach any frontend.
Type-safe config access
One of my quiet favourite additions: Config::string(), Config::integer(), Config::boolean() — typed config accessors that throw a TypeError if the value doesn’t match. This catches a class of runtime bugs that previously required either defensive casting or PHPStan to surface.
// Before
$timeout = (int) config('services.queue.timeout', 30);
// After
$timeout = Config::integer('services.queue.timeout', 30);
Small, but it’s the kind of change that makes a codebase more honest.
Pest is now the default
Laravel 12 ships with Pest as the default test runner. PHPUnit still works, but new projects use Pest out of the box. For anyone who hasn’t made the switch: do it. The ergonomics are significantly better, especially for feature tests.
it('returns a 422 when the email is missing', function () {
post('/api/register', ['password' => 'secret'])
->assertUnprocessable()
->assertJsonValidationErrorFor('email');
});
What I’ve changed in my own projects
After upgrading two client projects to Laravel 12, my takeaways:
- Adopt the typed config helpers everywhere. Set up PHPStan to flag the old
config()helper usage — the migration is mechanical but worthwhile. - Lean into Pest’s
arch()tests. Architecture testing catches dependency violations before they rot your codebase. - Don’t abandon Breeze/Jetstream immediately. For existing projects, migration risk outweighs the gains. Greenfield only.
The framework keeps moving in a direction I like: more guardrails, better defaults, fewer footguns. That’s all I ask of a framework I’m putting production workloads on.