---
title: "Naming conventions"
manual: "TYPO3 Explained"
version: "main"
permalink: "https://docs.typo3.org/permalink/t3coreapi:extension-naming@main"
source: "ExtensionArchitecture/BestPractises/NamingConventions.rst"
modified: "2026-09-16T13:05:25+00:00"
---

# Naming conventions

The first thing you should decide on is the [extension key](https://docs.typo3.org/permalink/t3coreapi:extension-key@main)
for your extension and the vendor name. A significant part of the names below
are based on the extension key.

> [!TIP]
> Some of the names, such as extension key or vendor name, will be spelled differently,
> depending on where they are used, for example:
>
> -   underscores (`_`) in the extension key should be replaced by dashes (`-`), when used in the
>     package name in the file [`composer.json`](../FileStructure/ComposerJson.md#file-extension-composer-json) (e.g. `cool_shop` becomes `<vendor>/cool-shop`)
> -   underscores in the extension key should be removed by converting the extension key
>     to UpperCamelCase in namespaces (e.g. `cool_shop` becomes `\MyVendor\CoolShop`)

## Abbreviations & glossary

-   **UpperCamelCase**

    [UpperCamelCase](https://en.wikipedia.org/wiki/Camel_case) begins
    with a capital letter and begins all following subparts of a word with a
    capital letter. The rest of each word is in lowercase with no spaces,
    e.g. `CoolShop`.

-   **lowerCamelCase**

    [lowerCamelCase](https://en.wikipedia.org/wiki/Camel_case) is
    the same as UpperCamelCase, but begins with a lowercase letter.

-   **TER**

    The ["TYPO3 Extension Repository"](https://extensions.typo3.org/):
    A catalogue of extensions where you can find information about
    extensions and where you can search and filter by TYPO3 version
    etc. Once registered on [https://my.typo3.org](https://my.typo3.org), you can login and register
    an extension key for your extension in [https://extensions.typo3.org](https://extensions.typo3.org)
    **My Extensions**.

-   **extkey**

    The extension key as is (e.g. 'my_extension').

-   **extkeyprefix**

    The extension key with stripped away underscores (e.g. extkey='my_extension'
    becomes extkeyprefix='myextension').

-   **ExtensionName**

    The term ExtensionName means the extension key in UpperCamelCase.

    Example: for an extkey `bootstrap_package` the ExtensionName would be `BootstrapPackage`.

    The ExtensionName is used as first parameter in the Extbase method
    `ExtensionUtility::configurePlugin()` and as value for the
    `extensionName` key when
    [registering a backend module](https://docs.typo3.org/permalink/t3coreapi:backend-modules-configuration@main).

-   **modkey**

    The backend module key.

-   **Public extensions**

    Public extensions are publicly available. They are usually registered in TER
    and available via [Packagist](https://packagist.org/).

-   **Private extensions**

    These are not published to the TER or Packagist.

Some of these "Conventions" are actually mandatory, meaning you **will** most likely
run into problems if you do not adhere to them.

We **very strongly recommend** to always use these naming conventions. Hard requirements
are emphasized by using the words MUST, etc. as specified in
[RFC 2119](https://www.ietf.org/rfc/rfc2119.txt). SHOULD or MAY indicate
a soft requirement: strongly recommended but will usually work, even if you
do not follow the conventions.

> [!TIP]
> If you study the naming conventions closely you will find that
> they are complicated due to varying rules derived from the extkey,
> if the extkey contains underscores. Sometimes the underscores are
> stripped off, sometimes not, sometimes a name in UpperCamelCase is created.
>
> The best practice you can follow is to  *avoid using underscores* in
> your extensions keys altogether. That will make the rules simpler and is
> highly recommended.

## Extension key (extkey)

The extension key (extkey) is used **as is** in:

-   directory name of extension in `typo3conf/ext`
    (or `typo3/sysext` for system extensions)

Derived names are:

-   package name in [`composer.json`](../FileStructure/ComposerJson.md#file-extension-composer-json) `<vendor-name>/<package-name>`.
    Underscores (`_`) should be replaced by dashes (`-`)
-   namespaces: Underscores in the extension key are removed by converting the extension key
    to UpperCamelCase in namespaces (e.g. `cool_shop` becomes `MyVendorCoolShop`).

> [!WARNING]
> **Attention**
>
> If you plan to [publish your extension](https://docs.typo3.org/permalink/t3coreapi:publish-extension@main),
> the extension key must be unique worldwide. This will be checked
> and enforced once you register the extension key on extensions.typo3.org.
>
> The *extkey* is valid if the TER accepts it. This also makes sure that the
> name follows the rules and is unique.
>
> Do this early! An already reserved key can usually only be transferred if the
> original author agrees to this.

1.  The *extkey* MUST be unique within your installation.
1.  The *extkey* MUST be made up of lowercase alphanumeric characters
    and underscores only and MUST start with a letter.
1.  More, see [extension key](https://docs.typo3.org/permalink/t3coreapi:extension-key@main)

-   **Examples for *extkeys*:**

    -   `cool_shop`
    -   `blog`

Examples for names that are derived from the extkey:

Here, the *extkey* is `my_extension`:

-   namespace: `MyVendor\MyExtension\...`
-   package name in [`composer.json`](../FileStructure/ComposerJson.md#file-extension-composer-json): `vendor-name/my-extension` (the underscore is replaced by
    a dash)

## Vendor name

The vendor name is used in:

-   namespaces
-   package name in [`composer.json`](../FileStructure/ComposerJson.md#file-extension-composer-json), e.g. `myvendor/cool-shop` (all lowercase)

> [!WARNING]
> **Attention**
>
> The vendor name MUST be unique (if you publish your extensions
> on packagist).
>
> Register your vendor name early on [Packagist](https://packagist.org)!

Use common PHP naming conventions for vendor names in namespaces and check
[PSR-0](https://www.php-fig.org/psr/psr-0/). There are currently no strict
rules, but commonly used vendor names begin with a capital letter,
followed by all lowercase.

The vendor name (as well as the *extkey*) is spelled with all lowercase when
used in the package name in the file [`composer.json`](../FileStructure/ComposerJson.md#file-extension-composer-json)

For the following examples, we assume:

-   the vendor name is `MyCompany`
-   the extkey is `my_example`

-   **Examples:**

    -   Namespace: `MyCompany\MyExample\...`
    -   package name (in [`composer.json`](../FileStructure/ComposerJson.md#file-extension-composer-json)): `my-company/my-example`

> [!NOTE]
> **See also**
>
> -   [PSR-0](https://www.php-fig.org/psr/psr-0/)

## Database table name

These rules apply to public extensions, but should be followed nevertheless.

Database table names **should** follow this pattern:

```none
tx_<extkeyprefix>_<table_name>
```

-   `<extkeyprefix>` is the extension key without underscores, so `foo_bar` becomes `foobar`
-   `<table_name>` should clearly describe the purpose of the table

Examples for an extension named `cool_shop`:

-   `tx_coolshop_product`
-   `tx_coolshop_category`

### Extbase domain model tables

Extbase domain model tables **should** follow this pattern:

```none
tx_<extkeyprefix>_domain_model_<model-name>
```

-   `<extkeyprefix>` is the extension key without underscores, so `foo_bar` becomes `foobar`
-   `<model-name>` should match the domain model name

Examples for Extbase domain models and table names of an extension named `cool_shop`:

| Domain model | Table name |
| --- | --- |
| `\Vendor\BlogExample\Domain\Model\Post` | `tx_blogexample_domain_model_post` |
| `\Vendor\CoolShop\Domain\Model\Tag` | `tx_coolshop_domain_model_tag` |
| `\Vendor\CoolShop\Domain\Model\ProcessedOrder` | `tx_coolshop_domain_model_processedorder` |
| `\Vendor\CoolShop\Domain\Model\Billing\Address` | `tx_coolshop_domain_model_billing_address` |

> [!TIP]
> Notice that the names above use the singular form, e.g. `post` and
> not `posts`. This is recommended, but not a requirement.
> However, if you do not follow the recommended patterns
> you may need [manual mapping](https://docs.typo3.org/permalink/t3coreapi:extbase-domain-model-mapping@main).

### MM-tables for multiple-multiple relations between tables

**MM** tables (for multiple-multiple relations between tables) follow these rules.

Extbase:

```none
# rule for Extbase
tx_<extkeyprefix>_domain_model_<model-name-1>_<model-name-2>_mm
# example: EXT:blog with relation between post and comment
tx_blogexample_domain_model_post_comment_mm
```

Non-Extbase tables usually use a similar rule, without the "domain_model" part:

```none
# recommendation for non-Extbase third party extensions
tx_<extkeyprefix>_<model-1>_<model-2>_mm
# Example
tx_myextension_address_category_mm

# example for TYPO3 core:
sys_category_record_mm
```

## Database column name

When extending a common table like `tt_content`, column names SHOULD
follow this pattern:

```none
tx_<extkeyprefix>_<column-name>
```

-   `<extkeyprefix>` is the extension key without underscores, so `foo_bar` becomes `foobar`
-   `<column-name>` purpose of the column, lower case letters with underscores

> [!TIP]
> If you work with column-names not following this convention (e.g. `colPos`), you have to add a [column mapping](https://docs.typo3.org/permalink/t3coreapi:extbase-domain-model-mapping@main).

## Backend module key (modkey)

The **main module key** SHOULD contain only lowercase characters. Do not use an
underscore or dash.

The **submodule key** MUST be made up of alphanumeric characters only. It MAY
contain underscores and MUST start with a letter.

-   **Example:**

    -   `Coolshop`

Example usage:

**EXT:my_extension/Configuration/Backend/Modules.php**

```php
<?php

declare(strict_types=1);

return [
  // Submodule key
  'content_productmanagement' => [
    // Main module key (use existing main module 'content' here)
    'parent' => 'content',
    // ...
  ],
];

```

> [!NOTE]
> **Changed in version 14.0**
>
> The main module `web` has been renamed to `content`.
> See [Feature: #107628 - Improved backend module naming and structure](https://docs.typo3.org/c/typo3/cms-core/main/en-us/Changelog/14.0/Feature-107628-ImprovedBackendModuleNamingAndStructure.html#feature-107628-1729026000)

For more details have a look into the [Modules.php - backend module configuration](https://docs.typo3.org/permalink/t3coreapi:backend-modules-configuration@main)
chapter.

## Backend module signature

The backend module signature is a derived identifier which is constructed by
TYPO3 when the module is registered.

The signature is usually constructed by using the [main module key and submodule
key](https://docs.typo3.org/permalink/t3coreapi:backendmodulekey@main), separated by an underscore.
Conversions, such as underscore to UpperCamelCase or conversions to lowercase
may be applied in this process.

Examples (from TYPO3 Core extensions):

-   web_info
-   web_FormFormbuilder
-   site_redirects

> [!TIP]
> You can look at existing module signatures in
> **System > Configuration > Backend Modules**.

## Plugin signature

> [!NOTE]
> **Changed in version 14.0**
>
> Adding frontend plugins as a "General Plugin", setting the content
> record `CType` to `'list'` and `list_type` to the plugin signature
> is not possible anymore. See [Migration](https://docs.typo3.org/m/typo3/reference-coreapi/13.4/en-us/ApiOverview/ContentElements/MigrationListType.html#plugins-list-type-migration).

The plugin signature of non-Extbase plugins, registered via
`ExtensionManagementUtility::addPlugin()`, is an arbitrary string.
By convention it should be the extension name with all underscores removed,
followed by one underscore and the alphanumeric plugin key in lowercase.
Examples: `"myextension_coolplugin"`, `"examples_pi1"`.

Extbase based plugins are registered via `ExtensionUtility::registerPlugin()`.
This method expects the extension key (UpperCamelCase or with underscores) as
the first parameter and a plugin name in UpperCamelCase (for example `"Pi1"` or
`"CoolPlugin"`). The method then returns the new plugin signature.

If you have to write the signature yourself in other contexts (TypoScript for
example) you can build it yourself from the extension name and the plugin name:

For this, all underscores in the extension key are omitted and all characters set to lowercase.
The extension key and plugin key are separated by an underscore (`_`).

Example:

**Plugin name and Plugin key listed**

```php
$extensionName = 'my_extension';
$pluginName = 'MyCoolPlugin';
$pluginSignature = "myextension_mycoolplugin"
```

The plugin signature is used in:

-   the database field `tt_content.CType`
-   in TypoScript, `plugin.tx_myexample_myplugin` to define settings
    for the plugin etc.
-   As [record type](https://docs.typo3.org/m/typo3/reference-tca/main/en-us/Types/Index.html#types) in TCA. It can therefore be used to
    define which fields should be visible in the TYPO3 backend.

### Example register and configure a non-Extbase plugin:

**EXT:examples/Configuration/TCA/Overrides/tt_content_plugin_htmlparser.php**

```php
<?php

use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;

$pluginSignature = 'examples_pi1';

// Add the plugins to the list of plugins
ExtensionManagementUtility::addPlugin(
  [
    'examples.db:tt_content.list_type_pi1',
    $pluginSignature,
  ],
  'FILE:EXT:examples/Configuration/FlexForms/Registration.xml',
);

```

**EXT:examples/Configuration/setup.typoscript**

```typoscript
plugin.tx_examples_pi1 {
  settings.pageId = 42
}
```

## Plugin key (Extbase only)

The plugin key is registered in:

-   second parameter in `ExtensionUtility::registerPlugin()`

The same plugin key is then used in the following:

-   second parameter in `ExtensionUtility::configurePlugin()`

The plugin key can be freely chosen by the extension author, but you **should**
follow these conventions:

-   do not use underscore
-   use UpperCamelCase, e.g. InventoryList
-   use alphanumeric characters

Avoid using meaningless keys like `Pi1`, `Pi2` etc. They can still be seen in
older extensions.

The plugin key used in `registerPlugin()` and `configurePlugin()`
**must** match or the later method will fail.

Method `ExtensionUtility::registerPlugin()` returns the [Plugin signature](https://docs.typo3.org/permalink/t3coreapi:naming-conventions-plugin-signature@main)
that can be used if additional TCA changes are required by the plugin, for
example to display additional fields.

### Example register and configure an Extbase plugin:

**EXT:examples/Configuration/TCA/Overrides/tt_content_plugin_htmlparser.php**

```php
<?php

use TYPO3\CMS\Extbase\Utility\ExtensionUtility;

$pluginSignature = ExtensionUtility::registerPlugin(
  'Examples',
  'HtmlParser',
  'examples.plugins.htmlparser:title',
  null,
  'plugins',
  'examples.plugins.htmlparser:description',
  'FILE:EXT:examples/Configuration/FlexForms/Registration.xml',
);

```

**EXT:examples/ext_localconf.php**

```php
<?php

use T3docs\Examples\Controller\HtmlParserController;
use TYPO3\CMS\Extbase\Utility\ExtensionUtility;

ExtensionUtility::configurePlugin(
  'Examples',
  'HtmlParser',
  [
    HtmlParserController::class => 'index',
  ],
  [
    HtmlParserController::class => 'index',
  ],
);

```

**EXT:examples/Configuration/setup.typoscript**

```typoscript
plugin.tx_examples_htmlparser {
  settings.pageId = 42
}
```

## Class name

Class names **should** be in UpperCamelCase.

Examples:

-   `CodeCompletionController`
-   `AjaxController`

> [!NOTE]
> **See also**
>
> This follows [PSR-1](https://www.php-fig.org/psr/psr-1/) conventions.

## Upgrade wizard identifier

You SHOULD use the following naming convention for the identifier:

`extKey_wizardName`

This is not enforced.

Please see [Wizard identifier](https://docs.typo3.org/permalink/t3coreapi:upgrade-wizards-identifier@main) in the Upgrade Wizard chapter
for further explanations.

## Note on "old" extensions

Some the "classic" extensions from before the extension structure came
about do not comply with these naming conventions. That is an
exception made for backwards compatibility. The assignment of new keys
from the TYPO3 Extension Repository will make sure that any of these
old names are not accidentally reassigned to new extensions.

Furthermore, some of the classic plugins (tt_board, tt_guest etc) use
the "user\_" prefix for their classes as well.

## Further reading

> [!NOTE]
> **See also**
>
> -   [extension key](https://docs.typo3.org/permalink/t3coreapi:extension-key@main)
> -   [Publish your extension](https://docs.typo3.org/permalink/t3coreapi:publish-extension@main)
> -   [Coding guidelines](https://docs.typo3.org/permalink/t3coreapi:cgl@main)
> -   [Xliff language file id naming conventions](https://docs.typo3.org/permalink/t3coreapi:xliff-id-naming@main)
