Breaking: #107823 - Strict typing and API cleanup in backend template components 

See forge#107823

Description 

The backend template components system (buttons, dropdown items, and menus) has been modernized with strict type hints, consistent return types, and improved architecture to enhance type safety and developer experience.

Impact 

Extensions that implement or extend backend template components must verify their type declarations and update any usage of changed methods.

New ComponentInterface 

A new \TYPO3\CMS\Backend\Template\Components\ComponentInterface has been introduced as the parent interface for both \ButtonInterface and \DropDownItemInterface. This unifies the common contract for all renderable backend components.

Both interfaces now extend ComponentInterface , which defines:

  • isValid(): bool
  • getType(): string
  • render(): string

Custom implementations of \ButtonInterface or \DropDownItemInterface will now trigger a \TypeError if these return types are missing.

PositionInterface enforced 

The \TYPO3\CMS\Backend\Template\Components\PositionInterface now enforces strict type hints:

  • getPosition(): string
  • getGroup(): int

This interface allows buttons to define their own fixed position and group, which automatically override the position and group parameters passed to ButtonBar::addButton().

Icon nullability 

Icons are now consistently nullable across all button types. The AbstractButton::$icon property and related getter/setter methods now use ?Icon instead of Icon.

This affects classes extending AbstractButton , including LinkButton , InputButton , and SplitButton .

Method signature changes 

Several methods now have stricter parameter types or modified signatures:

  • MenuItem::isValid() and Menu::isValid() no longer accept parameters
  • AbstractDropDownItem::render() now declares a string return type
  • Various setter methods now require strict type hints for their parameters

Return type consistency 

Abstract classes now use static return types for better inheritance support, while concrete implementations may use self or static depending on extensibility requirements.

SplitButton API improvement 

The SplitButton::getButton() method has been replaced with getItems(), which returns a type-safe SplitButtonItems DTO instead of an untyped array.

Old (removed):

public function getButton(): array  // Returns array with magic keys 'primary' and 'options'
Copied!

New:

public function getItems(): SplitButtonItems  // Returns typed DTO
Copied!

The SplitButtonItems DTO provides:

  • public readonly AbstractButton $primary - The primary action button
  • public readonly array $options - Array of option buttons

This change improves type safety and prevents runtime errors from accessing non-existent array keys.

Affected installations 

All TYPO3 instances with custom backend components, such as buttons, menus, or dropdown items, that extend or implement the affected interfaces are impacted.

Migration 

Extension authors should:

  1. Verify custom button implementations have correct return types on interface methods.
  2. Check custom classes extending abstract buttons use proper strict types.
  3. Update `isValid()` calls on MenuItem and Menu objects (remove the parameter).
  4. Handle nullable icons when working with getIcon() methods.

Example: implementing ButtonInterface 

use TYPO3\CMS\Backend\Template\Components\ButtonInterface;

// Before
class CustomButton implements ButtonInterface {
    public function isValid() { ... }
    public function render() { ... }
    public function getType() { ... }
}
Copied!
use TYPO3\CMS\Backend\Template\Components\ButtonInterface;

// After
class CustomButton implements ButtonInterface {
    public function isValid(): bool { ... }
    public function render(): string { ... }
    public function getType(): string { return static::class; }
}
Copied!

Example: working with MenuItem/Menu 

// Before
if ($menuItem->isValid($menuItem)) { ... }
Copied!
// After
if ($menuItem->isValid()) { ... }
Copied!

Example: nullable icons 

// Handle nullable icon return
$icon = $button->getIcon();  // Now returns ?Icon
$html = $icon?->render() ?? '';
Copied!

Example: using SplitButton with typed DTO 

If you were directly accessing the getButton() method:

// Before
$items = $splitButton->getButton();
$primary = $items['primary'];  // Magic string key
$options = $items['options'];  // Magic string key
Copied!
// After
$items = $splitButton->getItems();
$primary = $items->primary;  // Type-safe property access
$options = $items->options;  // Type-safe property access
Copied!