The Drupal 11.5 Route title property introduces a small but welcome improvement to the routing system. Route attributes now support a top-level title property, making route definitions cleaner and allowing simple dynamic page titles to be defined directly alongside the route.
If you're developing modules using PHP attributes, this change removes the need to place _title inside defaults and, in many cases, eliminates the need for a separate _title_callback.
What Changed?
Drupal's Drupal\Core\Routing\Attribute\Route extends Symfony's Route attribute and now includes a dedicated title property.
Previously, page titles had to be defined using the _title route default.
Before
use Symfony\Component\Routing\Attribute\Route;
#[Route(
path: '/admin/config/system/site-information',
name: 'system.site_information_settings',
defaults: [
'_title' => 'Basic site settings',
],
)]
With Drupal 11.5, the title becomes a first-class property.
After
use Drupal\Core\Routing\Attribute\Route;
use Drupal\Core\StringTranslation\TranslatableMarkup;
#[Route(
path: '/admin/config/system/site-information',
name: 'system.site_information_settings',
title: new TranslatableMarkup('Basic site settings'),
)]
The result is a cleaner and more expressive route definition.
Dynamic Titles Without _title_callback
One of the most useful additions is support for closures as route titles.
Previously, dynamic page titles required a separate callback.
Before
block_content.add_form:
path: '/block/add/{block_content_type}'
defaults:
_controller: '\Drupal\block_content\Controller\BlockContentController::addForm'
_title_callback: '\Drupal\block_content\Controller\BlockContentController::getAddFormTitle'
Now, the title logic can live directly next to the route.
After
#[Route(
path: '/block/add/{block_content_type}',
name: 'block_content.add_form',
title: static function (BlockContentTypeInterface $block_content_type) {
return new TranslatableMarkup(
'Add %type content block',
[
'%type' => $block_content_type->label(),
]
);
},
)]
This keeps related logic together and makes routes much easier to read.
What Can title Be?
The new property accepts three types of values:
- a plain string
- a
TranslatableMarkupobject - a static closure that returns the page title
For example:
title: 'Settings'
or
title: new TranslatableMarkup('Settings')
or
title: static function (...) {
return new TranslatableMarkup(...);
}
How Title Closures Work
The closure is evaluated on every request.
Its arguments are resolved exactly like a traditional _title_callback. Drupal automatically injects:
- upcasted route parameters
- the current request
- the route match
For example, if your route contains an entity parameter, Drupal will provide the loaded entity directly to the closure.
Limitations
There are two important restrictions.
No Container Access
Closures do not have access to Drupal's service container.
If generating the title requires injected services (for example, the current user, a custom service, or configuration), you should continue using _title_callback.
Method-Level Routes Only
Title closures only work on method-level #[Route] attributes.
Using a closure in a class-level route attribute will result in an InvalidArgumentException.
Why This Matters
Although the change is relatively small, it improves the developer experience in several ways:
- cleaner route definitions
- less boilerplate
- no separate title callback for simple dynamic titles
- route definition and title logic remain together
- easier to read and maintain code
For many controllers, this means one less method and one less routing default to maintain.
Migration Tips
When upgrading code to Drupal 11.5:
- Replace
defaults['_title']with the newtitleproperty. - Replace simple
_title_callbackmethods with inline closures where no services are required. - Keep
_title_callbackfor titles that depend on dependency injection.
Final Thoughts
The new title property is a small API enhancement that makes Drupal's PHP attribute routing more expressive and easier to maintain. By allowing both static and simple dynamic titles directly within the route definition, Drupal 11.5 reduces boilerplate while keeping related logic in one place.
If you're already using PHP attributes for routing, this is an easy improvement to adopt and a nice quality-of-life upgrade for module development.
This feature was introduced in the official Drupal 11.5 change record:
https://www.drupal.org/node/3607968
Comments