Defining routes for Extbase plugins
Each entry in the
routes list of the
Extbase plugin enhancer maps one
controller/action combination to a URL pattern. TYPO3 tries each entry in
order — first match wins, both for incoming requests and for URL generation.
See also
Route Enhancements and Aspects — route configuration keys —
full reference for
defaults,
requirements,
static, and
_arguments.
Anatomy of a route entry
routes:
- routePath: '/{conference_slug}'
_controller: 'Conference::show'
_arguments:
conference_slug: conference
routePath - The URL segment appended to the page slug. Static text and
{placeholder}variables can be combined freely. A bare/matches the page URL with nothing appended — useful for the default action. _controller- The controller/action pair in
Controllerformat — noName:: action Name Actionsuffix, no namespace. This tells TYPO3 which route to pick when generating a URL for that action, and which action to dispatch to when resolving an incoming request. _arguments-
Maps placeholder names in
routeto Extbase argument names. In the example above, the URL segment captured byPath {conference_is passed to the action as the argument namedslug} conference. The action receives the raw URL value — typically a slug string liketypo3camp-. An aspect on2025 conference_then translates that slug into the UID that Extbase uses to load the object. Without an aspect the raw string reaches the action unchanged.slug Omit
_argumentsentirely when the placeholder name already matches the Extbase argument name.
Route order and specificity
Routes are evaluated top to bottom. The first entry whose
route
pattern and
_controller both match wins. This means a general placeholder
route can accidentally swallow requests meant for a more specific route if it
appears first.
Consider a plugin with a list action, a paginated list, and a detail action.
Given the page slug /conferences, the following URLs need to resolve
correctly:
/conferences/→Conference::(no arguments)list /conferences/→page/ 2 Conference::withlist currentPage = 2 /conferences/→typo3camp- 2025 Conference::with the resolved UIDshow
With this route order, resolution works correctly:
routes:
- routePath: '/'
_controller: 'Conference::list'
- routePath: '/page/{page}'
_controller: 'Conference::list'
_arguments:
page: currentPage
- routePath: '/{conference_slug}'
_controller: 'Conference::show'
_arguments:
conference_slug: conference
If
/ were listed first, the request for
/conferences/ would match it —
conference_ would
receive the value page and the pagination route would never be reached.
The slug typo3camp- and the static segment page are both
just strings to the router; order is what distinguishes them.
Making path segments optional with defaults
The
defaults key at enhancer level makes a placeholder optional. When the
generated URL would contain the default value, that segment is omitted entirely.
In the full example below,
page: '1' means a link to page 1 of the
list produces /conferences/ rather than /conferences/.
A link to page 2 still produces /conferences/.
routeEnhancers:
ConferencesPlugin:
type: Extbase
limitToPages:
- 'page["module"] == "conferences"'
extension: MyExtension
plugin: Conferences
defaultController: 'Conference::list'
routes:
- routePath: '/'
_controller: 'Conference::list'
- routePath: '/page/{page}'
_controller: 'Conference::list'
_arguments:
page: currentPage
- routePath: '/{conference_slug}'
_controller: 'Conference::show'
_arguments:
conference_slug: conference
defaults:
page: '1'
requirements:
page: '\d+'
aspects:
page:
type: StaticRangeMapper
start: '1'
end: '100'
conference_slug:
type: PersistedAliasMapper
tableName: tx_myextension_domain_model_conference
routeFieldName: slug
Constraining placeholders with requirements
requirements defines a regular expression per placeholder. A route only
matches an incoming URL segment if it satisfies the requirement; without a
requirement, any string matches.
In the full example below,
page: '\ ensures that
/conferences/ matches the pagination route while
/conferences/ does not — because typo3camp-
is not all digits, so the pagination route is skipped and the detail route
matches instead.
Strict requirements also affect URL generation: combined with a
StaticRangeMapper aspect, they
allow TYPO3 to treat the parameter as static and omit cHash from the
generated URL.
Note that
requirements are ignored for any placeholder that has a
corresponding
aspects entry — the aspect takes precedence.
routeEnhancers:
ConferencesPlugin:
type: Extbase
limitToPages:
- 'page["module"] == "conferences"'
extension: MyExtension
plugin: Conferences
defaultController: 'Conference::list'
routes:
- routePath: '/'
_controller: 'Conference::list'
- routePath: '/page/{page}'
_controller: 'Conference::list'
_arguments:
page: currentPage
- routePath: '/{conference_slug}'
_controller: 'Conference::show'
_arguments:
conference_slug: conference
defaults:
page: '1'
requirements:
page: '\d+'
aspects:
page:
type: StaticRangeMapper
start: '1'
end: '100'
conference_slug:
type: PersistedAliasMapper
tableName: tx_myextension_domain_model_conference
routeFieldName: slug
The next step is configuring the
aspects entries that translate placeholder
values into human-readable URL segments — see Routing aspects: mapping route placeholders to URLs.