What Laravel 13 changes, and what it costs to get there
Laravel 13 landed on 17 March 2026. It requires PHP 8.3 and supports up to 8.5, receives bug fixes into Q3 2027 and security fixes until March 2028.
The headline is unusual for a major release: Laravel deliberately kept the breaking changes small. Most of the year's work shipped as minor releases that did not break anything, so the major itself is mostly additive. In our experience that claim holds — and it means the interesting question is not "can we upgrade" but "what is now worth using".
The framework upgrade is not where the work is
We moved this site to Laravel 13 recently. The framework portion was close to trivial. What actually cost time was the ecosystem around it: our CMS capped at Laravel 12, which forced a major CMS upgrade, which in turn forced two addon majors.
That is the pattern worth planning around. Before scheduling a Laravel 13 upgrade, check the laravel/framework constraint of every package you depend on. One package pinned to ^12.0 decides your timeline, not the framework.
AI as a first-party concern
The largest addition is the Laravel AI SDK: a provider-agnostic API for text generation, tool-calling agents, embeddings, audio and images.
use App\Ai\Agents\SalesCoach;
$response = SalesCoach::make()->prompt('Analyze this sales transcript...');
return (string) $response;
Embeddings are exposed through the string helper, which is a small detail with real consequences — it puts vector generation one call away from anywhere you already handle text:
use Illuminate\Support\Str;
$embeddings = Str::of('Napa Valley has great wine.')->toEmbeddings();
The value here is not that Laravel can call a model — any HTTP client could. It is that swapping providers stops being a rewrite.
Semantic search in the query builder
Paired with the SDK, vector similarity is now expressible directly in queries, backed by PostgreSQL and pgvector:
$documents = DB::table('documents')
->whereVectorSimilarTo('embedding', 'Best wineries in Napa Valley')
->limit(10)
->get();
For anyone who has wired up a retrieval pipeline by hand, collapsing it into a query-builder clause removes a genuinely awkward layer.
Attributes instead of boilerplate
Laravel 13 pushes PHP attributes much further across the framework. Controller middleware and authorization can now sit on the class and method they govern:
use Illuminate\Routing\Attributes\Controllers\Authorize;
use Illuminate\Routing\Attributes\Controllers\Middleware;
#[Middleware('auth')]
class CommentController
{
#[Middleware('subscribed')]
#[Authorize('create', [Comment::class, 'post'])]
public function store(Post $post)
{
// ...
}
}
Queue behaviour gained the same treatment with #[Tries], #[Backoff], #[Timeout] and #[FailOnTimeout].
We would offer one caution. Attributes colocate configuration with the code it affects, which is a real readability win. They also scatter routing and authorization rules across the codebase rather than keeping them in one file you can read top to bottom. Pick one convention per project and hold to it; the worst outcome is middleware declared in three different places.
Smaller things that matter
Queue routing moves per-job connection and queue defaults into one place, instead of repeating them at every dispatch site:
Queue::route(ProcessPodcast::class, connection: 'redis', queue: 'podcasts');
Cache::touch() extends an item's TTL without reading and rewriting its value — the obvious fix for the read-modify-write dance that sliding expiry used to require.
JSON:API resources are now first-party, covering serialization, relationship inclusion, sparse fieldsets and compliant headers. If you have hand-rolled that spec before, this deletes a lot of code.
Request forgery protection has been formalised as PreventRequestForgery, adding origin-aware verification alongside the existing token-based CSRF protection.
Should you upgrade
If you are on Laravel 12 and your dependencies allow it, yes — this is one of the cheapest majors Laravel has shipped, and staying current is how upgrades stay cheap.
If a dependency blocks you, the honest answer is that the blocker is your project, not Laravel. Budget for that package's major upgrade first and treat the framework bump as the easy step at the end. That is the order we found ourselves working in, and it is the order we would plan for next time.