Routing aspects: mapping route placeholders to URLs 

A route placeholder like {conference_slug} is just a named capture group — it matches any string segment in the URL. An aspect is what gives that placeholder meaning: it translates between the raw internal value (typically a UID) used by Extbase and the human-readable segment that appears in the URL.

Aspects are defined at enhancer level under the aspects key, keyed by the placeholder name they apply to.

PersistedAliasMapper 

The most common aspect for Extbase plugins, implemented by \TYPO3\CMS\Core\Routing\Aspect\PersistedAliasMapper . It looks up a database record by its slug field to resolve a URL segment to a UID, and vice versa when generating a URL.

EXT:my_extension/Configuration/Sets/MyExtension/route-enhancers.yaml
routeEnhancers:
  ConferencesPlugin:
    type: Extbase
    limitToPages:
      - 'page["module"] == "conferences"'
    extension: MyExtension
    plugin: Conferences
    defaultController: 'Conference::show'
    routes:
      - routePath: '/{conference_slug}'
        _controller: 'Conference::show'
        _arguments:
          conference_slug: conference
    aspects:
      conference_slug:
        type: PersistedAliasMapper
        tableName: tx_myextension_domain_model_conference
        routeFieldName: slug
        routeValuePrefix: '/'
Copied!
tableName
The database table that holds the records — typically the Extbase domain model table (tx_{extension}_domain_model_{name}).
routeFieldName
The field whose value is used as the URL segment. This should be a TCA type slug field so that values are guaranteed to be unique and URL-safe. Using a plain title field is possible but risky: special characters are not sanitised automatically, and duplicates cause resolution failures.
routeValuePrefix
Slug fields in TCA store values with a leading / (for example /typo3camp-2025). Set routeValuePrefix: '/' to strip that prefix from the URL segment. Omit it for non-slug fields.

When TYPO3 receives /conferences/typo3camp-2025, the mapper looks up the record with slug = '/typo3camp-2025' in the table and passes its UID to the action as the conference argument. When generating a URL for a conference with UID 42, it reads the slug field and inserts the value into the URL — no UID ever appears in the address bar.

PersistedPatternMapper 

Implemented by \TYPO3\CMS\Core\Routing\Aspect\PersistedPatternMapper , this combines multiple database fields into one URL segment. Useful when no slug field exists and adding one is not an option — for example when extending a third-party table.

EXT:my_extension/Configuration/Sets/MyExtension/route-enhancers.yaml
aspects:
  conference_slug:
    type: PersistedPatternMapper
    tableName: tx_myextension_domain_model_conference
    routeFieldPattern: '^(?P<title>.+)-(?P<uid>\d+)$'
    routeFieldResult: '{title}-{uid}'
Copied!
routeFieldPattern
A regular expression with named capture groups that matches the stored field value.
routeFieldResult
The template for the URL segment, using the named groups from routeFieldPattern. Appending the UID ({title}-{uid}) guarantees uniqueness even when titles are not unique.

The PersistedAliasMapper is preferred whenever a slug field is available — it is simpler and its output is cleaner. Use PersistedPatternMapper when you need to construct the URL segment from multiple fields or when upgrading from a realurl-era configuration that used title-plus-UID URLs.

StaticValueMapper 

Implemented by \TYPO3\CMS\Core\Routing\Aspect\StaticValueMapper , this maps a fixed set of values between their internal representation and a human-readable URL segment. Suitable for arguments that can only take a known list of values — status flags, type identifiers, named steps in a wizard.

EXT:my_extension/Configuration/Sets/MyExtension/route-enhancers.yaml
routeEnhancers:
  ConferencesPlugin:
    type: Extbase
    limitToPages:
      - 'page["module"] == "conferences"'
    extension: MyExtension
    plugin: Conferences
    defaultController: 'Conference::list'
    routes:
      - routePath: '/status/{status}'
        _controller: 'Conference::list'
    aspects:
      status:
        type: StaticValueMapper
        map:
          upcoming: 1
          running: 2
          past: 3
Copied!

The map keys are the URL segments; the values are what Extbase receives as the argument. A request to /conferences/status/upcoming passes 1 to the action. A URL generated for status 3 produces /conferences/status/past.

Because the full set of valid values is known, TYPO3 treats the parameter as static and omits cHash from the generated URL.

For multi-language sites, add a localeMap to vary the URL segments per language without changing the internal values:

EXT:my_extension/Configuration/Sets/MyExtension/route-enhancers.yaml
aspects:
  status:
    type: StaticValueMapper
    map:
      upcoming: 1
      running: 2
      past: 3
    localeMap:
      - locale: 'de_DE.*'
        map:
          bevorstehend: 1
          laufend: 2
          vergangen: 3
Copied!

StaticRangeMapper 

Implemented by \TYPO3\CMS\Core\Routing\Aspect\StaticRangeMapper , this declares that a placeholder accepts an integer within a fixed range. The primary use case is pagination. Unlike a bare \d+ requirement, a StaticRangeMapper marks the parameter as static, which eliminates cHash from paginated URLs.

EXT:my_extension/Configuration/Sets/MyExtension/route-enhancers.yaml
aspects:
  page:
    type: StaticRangeMapper
    start: '1'
    end: '100'
Copied!

Set end to the maximum sensible value for your use case. The example uses 100 pages; TYPO3 enforces a hard upper limit of 1000. Requests with a value outside the configured range do not match the route — TYPO3 returns a 404 rather than silently passing through an out-of-range value.

Handling deleted or hidden records 

When a PersistedAliasMapper cannot resolve a slug — because the record has been deleted, hidden, or its slug changed — TYPO3 returns a 404 by default. The fallbackValue property changes this behaviour.

EXT:my_extension/Configuration/Sets/MyExtension/route-enhancers.yaml
routeEnhancers:
  ConferencesPlugin:
    type: Extbase
    limitToPages:
      - 'page["module"] == "conferences"'
    extension: MyExtension
    plugin: Conferences
    defaultController: 'Conference::show'
    routes:
      - routePath: '/{conference_slug}'
        _controller: 'Conference::show'
        _arguments:
          conference_slug: conference
    aspects:
      conference_slug:
        type: PersistedAliasMapper
        tableName: tx_myextension_domain_model_conference
        routeFieldName: slug
        # null removes the argument from the route result entirely;
        # the action receives null and can redirect to the list
        fallbackValue: null
Copied!

fallbackValue: null removes the argument from the route result entirely. The action receives null for that argument instead of a 404. Declare the action parameter as nullable and handle it explicitly — for example by redirecting to the list with a flash message:

EXT:my_extension/Classes/Controller/ConferenceController.php
<?php

namespace MyVendor\MyExtension\Controller;

use MyVendor\MyExtension\Domain\Model\Conference;
use Psr\Http\Message\ResponseInterface;
use TYPO3\CMS\Core\Type\ContextualFeedbackSeverity;
use TYPO3\CMS\Extbase\Mvc\Controller\ActionController;

class ConferenceController extends ActionController
{
    public function showAction(?Conference $conference = null): ResponseInterface
    {
        if ($conference === null) {
            $this->addFlashMessage(
                'The requested conference could not be found.',
                '',
                ContextualFeedbackSeverity::WARNING,
            );
            return $this->redirect('list');
        }

        $this->view->assign('conference', $conference);
        return $this->htmlResponse();
    }
}
Copied!

A string fallbackValue passes that string as the argument value instead, which lets you show a default record rather than an error page.

Aspects take precedence over requirements 

If a placeholder has both a requirements entry and an aspects entry, the aspect wins — the requirements regex is ignored for that placeholder. The aspect defines all valid values implicitly, so an additional regex constraint would be redundant and is silently discarded.

With aspects configured, the next step is generating URLs from controller actions and Fluid templates — see Generating URLs with the UriBuilder.