Laravel 13: The Complete Update Guide — Every Feature, Every Change, With Code
Released on March 17, 2026 at Laracon EU by Taylor Otwell, Laravel 13 carries a straightforward promise: zero breaking changes for the majority of codebases, and a meaningful set of capabilities that make modern application development cleaner.
The headline story is not a single breaking feature. It is the graduation of the Laravel AI SDK from experimental to production-stable, positioning Laravel as the first major PHP framework with a first-party, provider-agnostic AI development layer built directly into the ecosystem. OneUptime
But that is only one piece of it. This blog covers every confirmed Laravel 13 update with before/after code, practical impact, and honest notes on what actually changes your daily workflow.
What Laravel 13 is NOT
Before the feature list — a clarification that saves confusion.
The core philosophy of this release centers on two principles: minimal disruption for existing applications, and meaningful capability additions for teams building modern software. Most Laravel 12 applications will complete the upgrade in under one hour, and the framework team has publicly described this as a zero-breaking-change release for the majority of codebases. OneUptime
This is not a rewrite. It is not Laravel reinventing itself. It is the framework removing patterns you were writing manually, one at a time, across twenty-plus improvements. Let's get into every single one.
1. PHP 8.3 Minimum Requirement
Laravel 13 drops PHP 8.2 and requires PHP 8.3 as the minimum version. Laravel
This is the only hard requirement change. Before upgrading, verify your server:
bash
php --version # Must return PHP 8.3.x or higher
PHP 8.3 brings typed class constants, the json_validate() function, the #[Override] attribute, and improved readonly property handling. Laravel 13 leverages these internally for better type safety across the framework.
2. First-Party AI SDK — Production Stable
The Laravel AI SDK is now a first-party, production-stable component of the framework. The SDK is designed to be provider-agnostic — it works with OpenAI and Anthropic out of the box, and switching between providers is a single configuration value change rather than a code rewrite. The unified interface abstracts away provider-specific API differences, retry logic, and error normalization. OneUptime
What it covers:
- Text generation
- Image creation
- Audio synthesis
- Embedding generation
- Tool-calling agents
- Vector stores
Text generation:
php
use App\Ai\Agents\SupportAgent;
$response = SupportAgent::make()
->prompt('Summarize this support ticket: ' . $ticket->body);
return (string) $response;
Embedding generation (for semantic search):
php
use Illuminate\Support\Facades\AI;
$embedding = AI::embed('Find products similar to this description');
$results = DB::table('products')
->whereVectorSimilarTo('embedding', $embedding)
->limit(10)
->get();
Switching providers — one config change:
php
// config/ai.php
'default' => env('AI_PROVIDER', 'openai'), // change to 'anthropic' anytime
No code changes. No refactoring. The SDK handles the difference internally.
3. PHP Attributes Across 15+ Locations
Laravel 13 introduces native PHP attribute syntax as an optional alternative to class property declarations across more than 15 locations in the framework — including models, controllers, jobs, commands, listeners, mailables, and notifications. GitHub
Models — before:
php
class User extends Model
{
protected $table = 'users';
protected $primaryKey = 'user_id';
protected $keyType = 'string';
public $incrementing = false;
protected $fillable = ['name', 'email', 'avatar'];
protected $hidden = ['password', 'remember_token'];
protected $casts = ['email_verified_at' => 'datetime'];
}
Models — after:
php
#[Table('users', key: 'user_id', keyType: 'string', incrementing: false)]
#[Fillable(['name', 'email', 'avatar'])]
#[Hidden(['password', 'remember_token'])]
#[Cast(['email_verified_at' => 'datetime'])]
class User extends Model {}
Commands:
php
// Before
class SendDailyDigest extends Command
{
protected $signature = 'digest:send {--force}';
protected $description = 'Send daily digest emails to subscribers';
}
// After
#[Signature('digest:send {--force}')]
#[Description('Send daily digest emails to subscribers')]
class SendDailyDigest extends Command {}
Controller middleware:
php
// Before — in constructor
public function __construct()
{
$this->middleware('auth');
$this->middleware('verified')->only(['create', 'store']);
}
// After — on the class
#[Middleware('auth')]
#[Middleware('verified', only: ['create', 'store'])]
class PostController extends Controller {}
This is completely optional. All new features including PHP Attributes are optional and fully backward compatible. Existing property-based configuration continues to work exactly as before. Laravel
4. Queue::route() — Centralized Queue Routing
The new Queue::route() method lets you define which queue and connection each job class uses from a single location in a service provider. Previously, teams either set queue properties on each job class or repeated the configuration at every dispatch site. Centralized routing separates infrastructure concerns from business logic and makes it straightforward to change queue topology without touching individual job classes. GitHub
Before — scattered across every job:
php
class ProcessPayment implements ShouldQueue
{
public $queue = 'payments';
public $connection = 'redis';
}
class SendWelcomeEmail implements ShouldQueue
{
public $queue = 'emails';
public $connection = 'sqs';
}
After — one place in AppServiceProvider:
php
use Illuminate\Support\Facades\Queue;
public function boot(): void
{
Queue::route([
ProcessPayment::class => 'payments@redis',
SendWelcomeEmail::class => 'emails@sqs',
GenerateReport::class => 'reports@database',
SyncInventory::class => 'sync@redis',
]);
}
Change your queue infrastructure without touching a single job class. This is how it should have always worked.
5. Debounceable Jobs
Debounceable jobs keep only the last dispatch in a window, so a user editing the same document ten times in thirty seconds triggers a single rebuild instead of ten. Apply the #[DebounceFor] attribute to any queued job, or debounce at the call site without touching the class. Laravel
On the job class:
php
#[DebounceFor(30)] // seconds
class RebuildSearchIndex implements ShouldQueue
{
public function __construct(public int $documentId) {}
public function debounceId(): string
{
return 'search-index-' . $this->documentId;
}
}
At the call site (without touching the class):
php
dispatch(new SyncExternalData($accountId))->debounceFor(30);
Real-world use cases: search index rebuilds, notification batching, report generation, webhook retries.
6. JSON:API — First-Party Support
Laravel 13 adds JSON:API resources through Illuminate\Http\Resources\JsonApi\JsonApiResource. If you build APIs for mobile apps or third-party clients, you can now generate JSON:API-shaped resources with first-party tooling instead of stitching the spec together yourself. The generated resource includes dedicated attributes and relationships properties — a much cleaner default if your API targets JSON:API. Laravel
php
use Illuminate\Http\Resources\JsonApi\JsonApiResource;
class UserResource extends JsonApiResource
{
public function attributes(Request $request): array
{
return [
'name' => $this->name,
'email' => $this->email,
];
}
public function relationships(Request $request): array
{
return [
'posts' => PostResource::collection($this->posts),
'role' => new RoleResource($this->role),
];
}
}
Response structure, relationship inclusion, sparse fieldsets, and compliant headers — all handled automatically. No third-party packages, no maintenance burden.
7. Typed Configuration
Laravel 13 introduces typed configuration retrieval. Instead of guessing whether config('app.debug') returns a string or a boolean, you can now declare expected types. If the value does not match the declared type, Laravel throws a ConfigTypeMismatchException at boot. This catches a class of bugs that previously only surfaced at runtime in specific code paths. Medium
php
// Before — returns mixed, you hope for the best
$debug = config('app.debug'); // string 'true'? boolean true? who knows
$name = config('app.name'); // string
$ttl = config('cache.ttl'); // int? string? null?
// After — explicit, type-safe
$debug = config()->boolean('app.debug'); // guaranteed boolean
$name = config()->string('app.name'); // guaranteed string
$ttl = config()->integer('cache.ttl'); // guaranteed integer
$ratio = config()->float('app.sample_rate'); // guaranteed float
Misconfigured environment variables now fail loudly at boot, not silently at the one user who triggers that specific code path three weeks after deployment.
8. Cache::touch() — TTL Extension Without Fetching
php
// Before — had to fetch the value just to re-set it
$value = Cache::get('user_session_' . $userId);
Cache::put('user_session_' . $userId, $value, now()->addHours(2));
// After — one operation, no round-trip
Cache::touch('user_session_' . $userId, now()->addHours(2));
Redis gets a single EXPIRE command. Memcached uses its native TOUCH operation. For session management, rate limiting, and token refresh flows, this eliminates unnecessary cache reads entirely.
9. Passkeys (WebAuthn) Authentication
Laravel Fortify integrates the stack behind Features::passkeys() and a passkeys section in config/fortify.php, so Fortify apps get the same endpoints and contracts without reimplementing glue. Server package, npm client, and Fortify line up on routes and contracts so passwordless auth stays portable across stacks. Laravel
php
// config/fortify.php
'features' => [
Features::registration(),
Features::resetPasswords(),
Features::passkeys(), // add this line
],
Users authenticate with Face ID, fingerprint, Windows Hello, or a hardware security key. The private key never leaves their device. Phishing attacks and credential-stuffing attacks stop working. No plugin. No workaround.
10. PreventRequestForgery — Smarter CSRF
Laravel 13 replaces the old CSRF middleware name with PreventRequestForgery and adds origin-aware request verification on top of the existing token-based CSRF system. Laravel
php
// bootstrap/app.php — automatically in new L13 apps
->withMiddleware(function (Middleware $middleware) {
$middleware->web(replace: [
\App\Http\Middleware\VerifyCsrfToken::class
=> \Illuminate\Foundation\Http\Middleware\PreventRequestForgery::class,
]);
})
Traditional CSRF tokens verify that the form came from your site. Origin verification checks the Origin header of every request on top of that. Two layers instead of one.
11. Reverb Database Driver — WebSockets Without Redis
php
// .env — no Redis cluster needed
BROADCAST_CONNECTION=reverb
REVERB_DB_CONNECTION=pgsql // or mysql
// config/reverb.php
'servers' => [
'reverb' => [
'driver' => 'database',
],
],
Real-time WebSocket features now run against your existing database. For small-to-medium applications, this removes Redis as a hard infrastructure requirement entirely.
12. MCP UI Apps — Tools With Full Interfaces
MCP tools were text-only until now. With MCP UI App support, a tool can render a self-contained HTML app inside a sandboxed iframe in the host. Scaffold one with a single artisan command. Laravel
bash
php artisan make:mcp-app DashboardApp
php
class DashboardApp extends AppResource
{
public function handle(Request $request): Response
{
return Response::view('mcp.dashboard-app', [
'title' => $this->title(),
]);
}
}
The view is HTML, JS, and CSS in one file with the MCP SDK already inlined. Build admin dashboards, data browsers, config panels — all rendered directly inside your AI agent interface.
13. Toast Notifications in Starter Kits
Generic "Saved" messages are out, proper toast notifications are in. All 21 starter kit variants now use specific, translatable messages like "Profile updated." or "Team created." Livewire kits use Flux's native Flux::toast, while Inertia kits use Sonner with a tidy Inertia::flash pattern that is identical across React, Svelte, and Vue. Laravel
Small change that makes every scaffolded app feel significantly more polished out of the box.
14. laravel new — Cleaner Output
Instead of a wall of Composer, npm, and artisan output, each phase now renders as its own task with clear pass or fail indicators thanks to Laravel Prompts' task() helper. Want the full output? Pass --verbose. Laravel
bash
laravel new my-app ✓ Creating application ✓ Generating application key ✓ Running migrations ✓ Installing frontend dependencies ✓ Installing Pest ✓ Initializing git repository
Every developer who has scrolled through 400 lines of Composer output looking for an error will appreciate this.
15. Horizon on Redis Cluster + ElastiCache Serverless
Horizon now runs cleanly on AWS ElastiCache Serverless and other Redis Cluster deployments. The Horizon prefix is automatically wrapped in a hash tag so all keys land on the same slot, and Horizon::use() registers cluster connections properly instead of extracting a single node. Standalone setups behave exactly as before. Laravel
php
// config/horizon.php
'use' => env('HORIZON_USE', 'default'), // cluster connection name
If you have been fighting Redis Cluster key-slot errors with Horizon on AWS, this is the fix.
16. Semantic Vector Search with pgvector
Native in the query builder, PostgreSQL-exclusive:
php
$results = DB::table('articles')
->whereVectorSimilarTo('embedding', $searchQuery)
->where('published', true)
->orderByVectorDistance('embedding', $searchQuery)
->limit(20)
->get();
No Pinecone. No Weaviate. No external service. AI-powered search is now standard Eloquent against PostgreSQL.
Complete Laravel 13 Upgrade Checklist
Pre-upgrade:
- Verify PHP 8.3+ is running on your server
- Check package compatibility: Livewire, Inertia, Filament, Spatie, Cashier, Horizon
- Run your full test suite — record current pass/fail count as baseline
- Drain all queues before deploying (serialization format changed)
Upgrade:
bash
# 1. Update composer.json # "laravel/framework": "^13.0" # "php": "^8.3" # 2. Run update composer update # 3. Clear all caches php artisan config:clear php artisan cache:clear php artisan view:clear php artisan route:clear # 4. Run migrations php artisan migrate
Post-upgrade review (three high-impact areas):
PreventRequestForgeryorigin config — check your CORS and origin settings- Cache
serializable_classesand cache prefix defaults - Any custom contracts or cache store implementations
Support window: Laravel 13 will follow suit: 18 months of bug fixes through Q3 2027, and 2 years of security fixes through Q1 2028. CopyProgramming
Wrapping Up
Laravel 13 is not one headline feature. It is sixteen smaller ones that each remove a pattern you were writing manually — scattered across queue management, API design, authentication, caching, configuration, and developer experience.
Laravel powers more than 960,000 websites and holds over 50% of the PHP framework market. It ranks first in the Stack Overflow Developer Survey for the fifth consecutive year. And releases like this are exactly why. GitHub
The upgrade is smooth. PHP 8.3 is the only hard requirement. Everything else is additive.
If you are planning a new project or upgrading an existing one and want a second pair of eyes on the process, feel free to reach out.
Tushar Modi — Full Stack Developer, Jaipur tusharmodi.in