Slugs / URL parts 

The main purpose of this type is to define parts of a URL path to generate and resolve URLs. The according database field is generated automatically.

Introduction 

The main purpose of this type is to define parts of a URL path to generate and resolve URLs.

With a URL like https://www.typo3.org/ch/community/values/core-values/ a URL slug is typically a part like /community or /community/values/core-values.

Within TYPO3, a slug is always part of the URL "path" - it does not contain scheme, host, HTTP verb, etc.

A slug is usually added to a TCA-based database table, for example pages. The TCA then contains rules for evaluation and definition.

When applied to pages a hierarchy of pages is often represented by a combined slug: From each page the characteristic last part of its slug is used as path segment. The segments are combined into one slug, separated by slashes. The resulting URL's path is therefore similar to the file paths in common operating systems.

However, slugs in TYPO3 are not limited to be separated by slashes. It is possible to use other separators. Furthermore, a single path segment may contain any sign allowed in URLs, including slashes.

It is not required to build hierarchical paths. It is possible to assign a single, unique path segment to each page in a deep page hierarchy. In TYPO3 the only requirement is that the slug for each page in a domain must be unique.

If a TCA table contains a field called "slug", it needs to be filled for every existing record. It can be shown and edited via regular backend forms, and is also evaluated during persistence via DataHandler.

The default behaviour of a slug is as follows:

  • A slug only contains characters which are allowed within URLs. Spaces, commas and other special characters are converted to a fallback character.
  • A slug is always lower-cased.
  • A slug is unicode-aware.

It is possible to build a default value from the rootline (very helpful for pages, or categorized slugs), but also to just generate a "speaking" segment from e.g. a news title.

Sanitation and Validation configuration options apply when persisting a record via DataHandler.

In the backend forms a validation happens by an AJAX call, which immediately checks any input and receives a new proposal in case the slug is already used.

Example: A basic slug field 

The slug of a talk is generated from its type and its title:

The slug of a workshop

The slug of a workshop

EXT:my_extension/Configuration/TCA/Overrides/205-tx_myextension_talk-slug.php
<?php

use MyVendor\MyExtension\Slug\TalkSlugPrefix;
use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;

defined('TYPO3') or die();

ExtensionManagementUtility::addTCAcolumns('tx_myextension_talk', [
  'slug' => [
    'label' => 'my_extension.db:talk.slug',
    'config' => [
      'type' => 'slug',
      'generatorOptions' => [
        // For example "workshop/write-your-first-form-element"
        'fields' => ['talk_type', 'title'],
      ],
      'appearance' => [
        'prefix' => TalkSlugPrefix::class . '->getPrefix',
      ],
      'eval' => 'uniqueInPid',
      // The slug repeats the title, which the search finds already
      'searchable' => false,
    ],
  ],
]);
ExtensionManagementUtility::addToAllTCAtypes(
  'tx_myextension_talk',
  'slug',
  '',
  'after:title',
);
Copied!

Example: A slug field with prefix hook 

This example uses a custom slug prefix hook via config['appearance']['prefix'] to show the path of the program in front of the slug of a talk:

The slug of a workshop with the path of the program in front

The slug of a workshop with the path of the program in front

EXT:my_extension/Configuration/TCA/Overrides/205-tx_myextension_talk-slug.php
<?php

use MyVendor\MyExtension\Slug\TalkSlugPrefix;
use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;

defined('TYPO3') or die();

ExtensionManagementUtility::addTCAcolumns('tx_myextension_talk', [
  'slug' => [
    'label' => 'my_extension.db:talk.slug',
    'config' => [
      'type' => 'slug',
      'generatorOptions' => [
        // For example "workshop/write-your-first-form-element"
        'fields' => ['talk_type', 'title'],
      ],
      'appearance' => [
        'prefix' => TalkSlugPrefix::class . '->getPrefix',
      ],
      'eval' => 'uniqueInPid',
      // The slug repeats the title, which the search finds already
      'searchable' => false,
    ],
  ],
]);
ExtensionManagementUtility::addToAllTCAtypes(
  'tx_myextension_talk',
  'slug',
  '',
  'after:title',
);
Copied!

Properties of the TCA column type slug 

Name Type Scope
array Display
userFunction Display
string (list of keywords) Display / Proc.
string Display / Proc.
array Display / Proc.
array Display / Proc.
string Display / Proc.
boolean Display / Proc.
array Display / Proc.
array Display / Proc.
array Display / Proc.
boolean Display / Proc.
boolean Display
appearance
Type
array
TCA path
$GLOBALS['TCA'][$table]['columns'][$field]['config']['appearance']
Scope
Display

Properties that only apply to how the field is displayed in the backend.

prefix
Type
userFunction
TCA path
$GLOBALS['TCA'][$table]['columns'][$field]['config']['appearance']['prefix']
Scope
Display

New in version 14.2

The user function now receives the full TCA field configuration (config) and the field name (fieldName) as additional parameters.

Provides a string that is displayed in front of the input field. The URL of the site is set by default if nothing has been defined.

Assign a user function. It receives two arguments:

  • The first argument is the parameters array containing

    • site - The current site object
    • languageId - The current language ID (int)
    • table - The table name (string)
    • row - The current record data (array)
    • fieldName - The name of the slug field (string)
    • config - The full TCA configuration array of the slug field
  • The second argument is the reference object TcaSlug .

The user function should return the string which is then used for display purposes.

Example: Set a slug field prefix
The slug of a workshop with the path of the program in front

The slug of a workshop with the path of the program in front

EXT:my_extension/Configuration/TCA/Overrides/205-tx_myextension_talk-slug.php
<?php

use MyVendor\MyExtension\Slug\TalkSlugPrefix;
use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;

defined('TYPO3') or die();

ExtensionManagementUtility::addTCAcolumns('tx_myextension_talk', [
  'slug' => [
    'label' => 'my_extension.db:talk.slug',
    'config' => [
      'type' => 'slug',
      'generatorOptions' => [
        // For example "workshop/write-your-first-form-element"
        'fields' => ['talk_type', 'title'],
      ],
      'appearance' => [
        'prefix' => TalkSlugPrefix::class . '->getPrefix',
      ],
      'eval' => 'uniqueInPid',
      // The slug repeats the title, which the search finds already
      'searchable' => false,
    ],
  ],
]);
ExtensionManagementUtility::addToAllTCAtypes(
  'tx_myextension_talk',
  'slug',
  '',
  'after:title',
);
Copied!

The user function can be implemented like this:

EXT:my_extension/Classes/Slug/TalkSlugPrefix.php
<?php

declare(strict_types=1);

namespace MyVendor\MyExtension\Slug;

use TYPO3\CMS\Backend\Form\FormDataProvider\TcaSlug;
use TYPO3\CMS\Core\Site\Entity\Site;

/**
 * Shows the path of the program in front of the slug of a talk
 */
final class TalkSlugPrefix
{
  /**
   * @param array{site: Site} $parameters
   */
  public function getPrefix(array $parameters, TcaSlug $reference): string
  {
    return rtrim((string)$parameters['site']->getBase(), '/') . '/program/';
  }
}
Copied!
eval
Type
string (list of keywords)
TCA path
$GLOBALS['TCA'][$table]['columns'][$field]['config']['eval']
Scope
Display / Proc.

Configuration of field evaluation.

Keywords:

unique
Evaluate if a record is unique in the whole TYPO3 installation (specific to a language). This option is recommended as it allows to show any record stored inside other sites. The only downside is that it is not possible to have the same slug on multiple sites.
uniqueInSite

Requires the field to be unique for the current site and language. This allows for multiple records of the same table to have the same slug as long as these records are separated by their sites. Consequently records of a foreign site are not accessible with uniqueInsite since slugs are looked up respecting the current site.

uniqueInPid
Requires the field to be unique for the current PID among other records on the same page.

No other eval setting is checked for. It is possible to set different eval options, however it is recommended not to do so.

Example: eval = uniqueInSite
The slug of a conference

The slug of a conference

EXT:my_extension/Configuration/TCA/Overrides/105-tx_myextension_conference-slug.php
<?php

use MyVendor\MyExtension\Slug\ConferenceSlugModifier;
use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;

defined('TYPO3') or die();

ExtensionManagementUtility::addTCAcolumns('tx_myextension_conference', [
  'slug' => [
    'label' => 'my_extension.db:conference.slug',
    'config' => [
      'type' => 'slug',
      'generatorOptions' => [
        // The short title, or the title if there is no short title
        'fields' => [['short_title', 'title']],
        'replacements' => [
          '&' => 'and',
        ],
        'regexReplacements' => [
          // A year at the end of the title, the modifier adds it in front
          '/\s+\d{4}$/' => '',
        ],
        'postModifiers' => [
          ConferenceSlugModifier::class . '->prependYear',
        ],
      ],
      'fallbackCharacter' => '-',
      'eval' => 'uniqueInSite',
    ],
  ],
]);
ExtensionManagementUtility::addToAllTCAtypes(
  'tx_myextension_conference',
  'slug',
  '',
  'after:title',
);
Copied!
fallbackCharacter
Type
string
Default
-
TCA path
$GLOBALS['TCA'][$table]['columns'][$field]['config']['fallbackCharacter']
Scope
Display / Proc.

Character that represents the separator of slug sections, that contain the fieldSeparator.

Example: fallbackCharacter and fieldSeparator set
EXT:my_extension/Configuration/TCA/Overrides/105-tx_myextension_conference-slug.php
<?php

use MyVendor\MyExtension\Slug\ConferenceSlugModifier;
use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;

defined('TYPO3') or die();

ExtensionManagementUtility::addTCAcolumns('tx_myextension_conference', [
  'slug' => [
    'label' => 'my_extension.db:conference.slug',
    'config' => [
      'type' => 'slug',
      'generatorOptions' => [
        // The short title, or the title if there is no short title
        'fields' => [['short_title', 'title']],
        'replacements' => [
          '&' => 'and',
        ],
        'regexReplacements' => [
          // A year at the end of the title, the modifier adds it in front
          '/\s+\d{4}$/' => '',
        ],
        'postModifiers' => [
          ConferenceSlugModifier::class . '->prependYear',
        ],
      ],
      'fallbackCharacter' => '-',
      'eval' => 'uniqueInSite',
    ],
  ],
]);
ExtensionManagementUtility::addToAllTCAtypes(
  'tx_myextension_conference',
  'slug',
  '',
  'after:title',
);
Copied!
generatorOptions
Type
array
TCA path
$GLOBALS['TCA'][$table]['columns'][$field]['config']['generatorOptions']
Scope
Display / Proc.

Holds information about the record fields to be used for slug generation:

fields
Type
array
TCA path
$GLOBALS['TCA'][$table]['columns'][$field]['config']['generatorOptions']['fields']
Scope
Display / Proc.

Insert several field names (of type string) that will be considered during slug construction.

Can also be used as nested array to combine multiple fields [['nav_title', 'title'], 'other_field'] .

Examples:

Configuration value Values of an example page record Resulting slug
[['nav_title', 'title']] ['title' => 'Products', 'nav_title' => ''] /products
[['title', 'subtitle']] ['title' => 'Products', 'subtitle' => 'Product subtitle'] /products
['title', 'subtitle'] or [['title'], ['subtitle']] ['title' => 'Products', 'subtitle' => 'Product subtitle'] /products/product-subtitle
['nav_title', 'title'], 'subtitle' ['title' => 'Products', 'nav_title' => 'Best products', 'subtitle' => 'Product subtitle'] /best-products/product-subtitle
['seo_title', 'title'], ['nav_title', 'subtitle'] ['title' => 'Products', 'nav_title' => 'Best products', 'subtitle' => 'Product subtitle', 'seo_title' => 'SEO product title'] /seo-product-title/best-products
fieldSeparator
Type
string
Default
"/"
TCA path
$GLOBALS['TCA'][$table]['columns'][$field]['config']['generatorOptions']['fieldSeparator']
Scope
Display / Proc.

This value will divide the slug parts. If a section value contains this very value, it will be replaced by the value given in fallbackCharacter.

prefixParentPageSlug
Type
boolean
Default
true
TCA path
$GLOBALS['TCA'][$table]['columns'][$field]['config']['generatorOptions']['prefixParentPageSlug']
Scope
Display / Proc.

The slugs of parent pages will be prefixed to the slug for the page itself. Disable it for shorter URLs, but take the higher chance of collision into consideration.

regexReplacements
Type
array
TCA path
$GLOBALS['TCA'][$table]['columns'][$field]['config']['generatorOptions']['regexReplacements']
Scope
Display / Proc.
Added
14.0

A config array to provide regex based definitions for string replacement. Case-insensitive and wildcard replacement is possible.

The slug of a conference leaves out a year at the end of the title:

EXT:my_extension/Configuration/TCA/Overrides/105-tx_myextension_conference-slug.php
<?php

use MyVendor\MyExtension\Slug\ConferenceSlugModifier;
use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;

defined('TYPO3') or die();

ExtensionManagementUtility::addTCAcolumns('tx_myextension_conference', [
  'slug' => [
    'label' => 'my_extension.db:conference.slug',
    'config' => [
      'type' => 'slug',
      'generatorOptions' => [
        // The short title, or the title if there is no short title
        'fields' => [['short_title', 'title']],
        'replacements' => [
          '&' => 'and',
        ],
        'regexReplacements' => [
          // A year at the end of the title, the modifier adds it in front
          '/\s+\d{4}$/' => '',
        ],
        'postModifiers' => [
          ConferenceSlugModifier::class . '->prependYear',
        ],
      ],
      'fallbackCharacter' => '-',
      'eval' => 'uniqueInSite',
    ],
  ],
]);
ExtensionManagementUtility::addToAllTCAtypes(
  'tx_myextension_conference',
  'slug',
  '',
  'after:title',
);
Copied!
replacements
Type
array
TCA path
$GLOBALS['TCA'][$table]['columns'][$field]['config']['generatorOptions']['replacements']
Scope
Display / Proc.

It allows to replace strings of a slug part. Add one of more array items with the key being the string to replace and the value being the replacement string.

EXT:my_extension/Configuration/TCA/Overrides/105-tx_myextension_conference-slug.php
<?php

use MyVendor\MyExtension\Slug\ConferenceSlugModifier;
use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;

defined('TYPO3') or die();

ExtensionManagementUtility::addTCAcolumns('tx_myextension_conference', [
  'slug' => [
    'label' => 'my_extension.db:conference.slug',
    'config' => [
      'type' => 'slug',
      'generatorOptions' => [
        // The short title, or the title if there is no short title
        'fields' => [['short_title', 'title']],
        'replacements' => [
          '&' => 'and',
        ],
        'regexReplacements' => [
          // A year at the end of the title, the modifier adds it in front
          '/\s+\d{4}$/' => '',
        ],
        'postModifiers' => [
          ConferenceSlugModifier::class . '->prependYear',
        ],
      ],
      'fallbackCharacter' => '-',
      'eval' => 'uniqueInSite',
    ],
  ],
]);
ExtensionManagementUtility::addToAllTCAtypes(
  'tx_myextension_conference',
  'slug',
  '',
  'after:title',
);
Copied!

A conference titled Tips & Tricks Day is given a slug ending in tips-and-tricks-day instead of tips-tricks-day.

The replacements are applied to each field of fields on its own, before the parts are joined with the fieldSeparator. They only apply while a slug is generated from other fields. A slug typed in by an editor is left alone.

Changed in version 15.0 Important: #110501 - Slashes in page titles no longer end up in generated slugs

Slugs of existing pages only change when they are generated anew.

The pages table replaces a slash in the page title with -:

EXT:core/Configuration/TCA/pages.php (excerpt)
'replacements' => [
  '/' => '-',
],
Copied!

A page titled Support 24/7 is given the slug /support-24-7. With the slash kept, it would be /support-24/7, which looks like a subpage 7 of a page /support-24.

To keep slashes in generated page slugs, unset the option:

EXT:my_extension/Configuration/TCA/Overrides/pages.php
unset($GLOBALS['TCA']['pages']['columns']['slug']['config']['generatorOptions']['replacements']);
Copied!
postModifiers
Type
array
TCA path
$GLOBALS['TCA'][$table]['columns'][$field]['config']['generatorOptions']['postModifiers']
Scope
Display / Proc.

The "slug" TCA type includes a possibility to hook into the generation of a slug via custom TCA generation options.

A hook is registered as <class name>-><method name>, for example for the slug of a conference:

EXT:my_extension/Configuration/TCA/Overrides/105-tx_myextension_conference-slug.php
<?php

use MyVendor\MyExtension\Slug\ConferenceSlugModifier;
use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;

defined('TYPO3') or die();

ExtensionManagementUtility::addTCAcolumns('tx_myextension_conference', [
  'slug' => [
    'label' => 'my_extension.db:conference.slug',
    'config' => [
      'type' => 'slug',
      'generatorOptions' => [
        // The short title, or the title if there is no short title
        'fields' => [['short_title', 'title']],
        'replacements' => [
          '&' => 'and',
        ],
        'regexReplacements' => [
          // A year at the end of the title, the modifier adds it in front
          '/\s+\d{4}$/' => '',
        ],
        'postModifiers' => [
          ConferenceSlugModifier::class . '->prependYear',
        ],
      ],
      'fallbackCharacter' => '-',
      'eval' => 'uniqueInSite',
    ],
  ],
]);
ExtensionManagementUtility::addToAllTCAtypes(
  'tx_myextension_conference',
  'slug',
  '',
  'after:title',
);
Copied!

The method receives an array with the following keys:

slug
The slug to be used
workspaceId
The workspace ID, 0 in the live workspace
configuration
The configuration of the TCA field
record
The full record to be used
pid
The resolved parent page ID
prefix
The prefix that was added
tableName
The table of the slug field
fieldName
The name of the slug field

The modifier puts the year of the conference in front of the slug:

EXT:my_extension/Classes/Slug/ConferenceSlugModifier.php
<?php

declare(strict_types=1);

namespace MyVendor\MyExtension\Slug;

/**
 * Puts the year of the conference in front of the slug, for example
 * "2027/typo3-developer-days"
 */
final class ConferenceSlugModifier
{
  /**
   * @param array{slug: string, record: array<string, mixed>} $parameters
   */
  public function prependYear(array $parameters): string
  {
    $date = $parameters['record']['conference_date'] ?? '';
    // A timestamp from the database, or a date such as "2027-05-12" from
    // the backend form
    $year = is_numeric($date) && (int)$date > 0
      ? date('Y', (int)$date)
      : substr((string)$date, 0, 4);
    if (preg_match('/^\d{4}$/', $year) !== 1) {
      return $parameters['slug'];
    }
    return $year . '/' . ltrim($parameters['slug'], '/');
  }
}
Copied!

All hooks need to return the modified slug value.

Example: Generate a concatenated slug

The slug of a talk joins its type and its title with the field separator /:

The slug of a workshop

The slug of a workshop

EXT:my_extension/Configuration/TCA/Overrides/205-tx_myextension_talk-slug.php
<?php

use MyVendor\MyExtension\Slug\TalkSlugPrefix;
use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;

defined('TYPO3') or die();

ExtensionManagementUtility::addTCAcolumns('tx_myextension_talk', [
  'slug' => [
    'label' => 'my_extension.db:talk.slug',
    'config' => [
      'type' => 'slug',
      'generatorOptions' => [
        // For example "workshop/write-your-first-form-element"
        'fields' => ['talk_type', 'title'],
      ],
      'appearance' => [
        'prefix' => TalkSlugPrefix::class . '->getPrefix',
      ],
      'eval' => 'uniqueInPid',
      // The slug repeats the title, which the search finds already
      'searchable' => false,
    ],
  ],
]);
ExtensionManagementUtility::addToAllTCAtypes(
  'tx_myextension_talk',
  'slug',
  '',
  'after:title',
);
Copied!
Example: Generate a slug from one field with a backup field if first is empty

The slug of a conference uses the short title. If the short title is empty, it uses the title:

The slug of a conference

The slug of a conference

EXT:my_extension/Configuration/TCA/Overrides/105-tx_myextension_conference-slug.php
<?php

use MyVendor\MyExtension\Slug\ConferenceSlugModifier;
use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;

defined('TYPO3') or die();

ExtensionManagementUtility::addTCAcolumns('tx_myextension_conference', [
  'slug' => [
    'label' => 'my_extension.db:conference.slug',
    'config' => [
      'type' => 'slug',
      'generatorOptions' => [
        // The short title, or the title if there is no short title
        'fields' => [['short_title', 'title']],
        'replacements' => [
          '&' => 'and',
        ],
        'regexReplacements' => [
          // A year at the end of the title, the modifier adds it in front
          '/\s+\d{4}$/' => '',
        ],
        'postModifiers' => [
          ConferenceSlugModifier::class . '->prependYear',
        ],
      ],
      'fallbackCharacter' => '-',
      'eval' => 'uniqueInSite',
    ],
  ],
]);
ExtensionManagementUtility::addToAllTCAtypes(
  'tx_myextension_conference',
  'slug',
  '',
  'after:title',
);
Copied!
Example: Move the year of a conference to the front

The title 'TYPO3 Developer Days 2027' is given the slug '2027/typo3-developer-days'. The regular expression removes the year at the end of the title, and the modifier adds the year of the conference date in front:

The slug of a conference

The slug of a conference

EXT:my_extension/Configuration/TCA/Overrides/105-tx_myextension_conference-slug.php
<?php

use MyVendor\MyExtension\Slug\ConferenceSlugModifier;
use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;

defined('TYPO3') or die();

ExtensionManagementUtility::addTCAcolumns('tx_myextension_conference', [
  'slug' => [
    'label' => 'my_extension.db:conference.slug',
    'config' => [
      'type' => 'slug',
      'generatorOptions' => [
        // The short title, or the title if there is no short title
        'fields' => [['short_title', 'title']],
        'replacements' => [
          '&' => 'and',
        ],
        'regexReplacements' => [
          // A year at the end of the title, the modifier adds it in front
          '/\s+\d{4}$/' => '',
        ],
        'postModifiers' => [
          ConferenceSlugModifier::class . '->prependYear',
        ],
      ],
      'fallbackCharacter' => '-',
      'eval' => 'uniqueInSite',
    ],
  ],
]);
ExtensionManagementUtility::addToAllTCAtypes(
  'tx_myextension_conference',
  'slug',
  '',
  'after:title',
);
Copied!
prependSlash
Type
boolean
TCA path
$GLOBALS['TCA'][$table]['columns'][$field]['config']['prependSlash']
Scope
Display / Proc.

Defines whether a slug field should contain a prepending slash, for example for nested categories with speaking segments.

If not set, this defaults to false . (Exception: for the pages.slug field this defaults to true and cannot be changed.)

searchable
Type
boolean
Default
true
TCA path
$GLOBALS['TCA'][$table]['columns'][$field]['config']['searchable']
Scope
Display

New in version 14.0

TYPO3 now automatically includes all fields of suitable types in backend search operations in the Content > Records module.

This option can be used to explicitly exclude fields from being searchable, instead of having to list them in the now removed option $GLOBALS['TCA'][$table]['ctrl']['searchFields'].

By default, all fields of type slug are considered searchable. To exclude a field from being searchable, set the following in the field's TCA configuration:

EXT:my_extension/Configuration/TCA/Overrides/205-tx_myextension_talk-slug.php
<?php

use MyVendor\MyExtension\Slug\TalkSlugPrefix;
use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;

defined('TYPO3') or die();

ExtensionManagementUtility::addTCAcolumns('tx_myextension_talk', [
  'slug' => [
    'label' => 'my_extension.db:talk.slug',
    'config' => [
      'type' => 'slug',
      'generatorOptions' => [
        // For example "workshop/write-your-first-form-element"
        'fields' => ['talk_type', 'title'],
      ],
      'appearance' => [
        'prefix' => TalkSlugPrefix::class . '->getPrefix',
      ],
      'eval' => 'uniqueInPid',
      // The slug repeats the title, which the search finds already
      'searchable' => false,
    ],
  ],
]);
ExtensionManagementUtility::addToAllTCAtypes(
  'tx_myextension_talk',
  'slug',
  '',
  'after:title',
);
Copied!