Laravel 13.21 adds a #[RouteKey] class attribute for customizing route model binding on Eloquent models, a base64 validation rule, a #[RequestAttribute] contextual attribute for injecting request attributes, and PNG, GIF, AVIF, and BMP output support in the Image component. The Laravel team tagged v13.21.0 and v13.21.1 on July 21, 2026; the patch tag only bumps the framework's version constant, so the two are covered together here.
#[RouteKey]attribute for Eloquent route model binding- New
base64validation rule #[RequestAttribute]contextual attribute- PNG, GIF, AVIF, and BMP output formats for the Image component
- Customizable application builder and an
illuminate/concurrencysubsplit
What's New
#[RouteKey] Attribute for Eloquent Models
Customizing the column used for route model binding has always meant overriding getRouteKeyName() on the model. Following the pattern set by #[ObservedBy] and #[ScopedBy], models can now declare the route key with a class attribute instead:
use Illuminate\Database\Eloquent\Attributes\RouteKey;
#[RouteKey('slug')]
class Post extends Model
{
// ...
}
With the attribute in place, implicit route model binding resolves Post by its slug column, and getRouteKeyName() falls back to the primary key when no attribute is present. Contributed by @nimnaherath in #60841.
base64 Validation Rule
A base64 rule joins the validator's string format checks, filling a gap that libraries like Zod already cover. The rule verifies that a value is a valid RFC 4648 Base64 string:
$request->validate([
'signature' => ['required', 'base64'],
]);
The check requires a canonical encoding: the value must decode in strict mode and re-encode to the exact same string, so padding mistakes and stray characters fail validation. Contributed by @lucasmichot in #60808.
#[RequestAttribute] Contextual Attribute
Middleware often stashes resolved objects on the request's attribute bag, a tenant or organization resolved from an API key, for example. Pulling those back out means calling $request->attributes->get() and type-hinting by hand. The new #[RequestAttribute] contextual attribute injects the value directly into a resolved parameter:
use Illuminate\Container\Attributes\RequestAttribute;
class InventoryController
{
public function index(#[RequestAttribute('org')] Organization $org)
{
return $this->inventoryService->getForOrg($org);
}
}
Contributed by @cosmastech in #60847.
More Output Formats for the Image Component
The Image component introduced in Laravel 13.20 could only convert to WebP, JPG, and JPEG through its fluent methods, even though the bundled Intervention Image encoders support more. This release adds toPng(), toGif(), toAvif(), and toBmp():
use Illuminate\Support\Facades\Image;
Image::fromUpload($request->file('avatar'))
->cover(400, 400)
->toAvif()
->quality(80)
->store('avatars');
The change also fixes Image::extension(), which was missing the image/avif MIME mapping and would have produced a .bin extension for stored AVIF output. Contributed by @Tresor-Kasenda in #60713.
Customizable Application Builder
Application::configure() previously hardcoded the ApplicationBuilder class, so packages extending Laravel's Application had to override the whole method to swap it out. A protected static $applicationBuilder property now lets subclasses provide their own builder class while keeping the existing API. See #60848.
Concurrency Subsplit
The Illuminate\Concurrency component now has its own read-only subsplit, joining the other Illuminate components published as standalone packages. See #60836.
Other Fixes and Improvements
- Database transaction rollback callbacks now fire correctly (#60777), and a new lost connection message is detected for automatic reconnects (#60819)
- Question marks are escaped in
Grammar::whereColumn()(#60832) InvalidPayloadExceptionmessages now include the job name and queue (#60799)- Passing an enum to
LogManager::forgetChannel()no longer throws aTypeError(#60801), andRedisTaggedCache::decrement()handles enum keys (#60821) Str::wordWrap()handles multibyte strings correctly (#60814)- Contextual attribute caching no longer collides across properties of the same class (#60815), and falsey concurrency exception parameters are preserved (#60822)
- Fixed host port parsing in the
servecommand (#60828) and an undefined index inPipeline\Hub::pipe()(#60802)
References
- Official changelog
- Compare v13.20.0...v13.21.0
- Compare v13.21.0...v13.21.1
- PR: #60841 (
#[RouteKey]attribute) - PR: #60808 (
base64validation rule) - PR: #60847 (
#[RequestAttribute]contextual attribute) - PR: #60713 (Image component output formats)
- PR: #60848 (Customizable application builder)
0 Comments
Log in to join the conversation.No comments yet. Be the first to share your thoughts.