Now liveThe Skillselion MCP - thousands of ranked skills, loaded into your agent mid-task. No install.Get it →
dirnbauer avatar

Typo3 Update

  • 77 installs
  • 33 repo stars
  • Updated July 27, 2026
  • dirnbauer/webconsulting-skills

Develop and modernize TYPO3 v14 extensions using current Core APIs for controllers, ViewFactory, TCA, events, and QueryBuilder.

About

This skill guides TYPO3 v14 extension development and modernization, covering version constraints, controllers, Fluid, events, TCA, and QueryBuilder. A developer uses it when building or modernizing extensions against current v14 Core APIs.

  • TYPO3 v14 extension development: version constraints, controllers, ViewFactory, TCA
  • Covers events, backend modules, QueryBuilder, CLI commands, and v14-only changes

Typo3 Update by the numbers

  • 77 all-time installs (skills.sh)
  • Ranked #3,060 of 4,347 Backend & APIs skills by installs in the Skillselion catalog
  • Data as of Jul 29, 2026 (Skillselion catalog sync)
npx skills add https://github.com/dirnbauer/webconsulting-skills --skill typo3-update

Add your badge

Show developers this skill is listed on Skillselion. Paste this into your README.

Listed on Skillselion
Installs77
repo stars33
Last updatedJuly 27, 2026
Repositorydirnbauer/webconsulting-skills

What it does

Develop and modernize TYPO3 v14 extensions using current Core APIs for controllers, ViewFactory, TCA, events, and QueryBuilder.

Files

SKILL.mdMarkdownGitHub ↗

TYPO3 v14 development

Source: https://github.com/dirnbauer/webconsulting-skills
Strategy: This collection targets TYPO3 v14.x only. Patterns here assume a v14 codebase.
For migrating from older majors, use typo3-rector, typo3-extension-upgrade, Fractor, and the official upgrade guide first.
TYPO3 API First: Always use TYPO3's built-in APIs, core features, and established conventions before creating custom implementations. Do not reinvent what TYPO3 already provides. Always verify that the APIs and methods you use exist and are not deprecated in TYPO3 v14 by checking the official TYPO3 documentation.

1. Version Strategy

Target: TYPO3 v14.x only

VersionRole for this collection
v14.xSupported target — examples and constraints assume TYPO3 v14
Older majorsNot targeted — complete a core upgrade, then use these patterns

Best Practice: Declare TYPO3 v14-only constraints in composer.json and ext_emconf.php for new work.

Version Constraints

<?php
// ext_emconf.php — TYPO3 v14
$EM_CONF[$_EXTKEY] = [
    'title' => 'My Extension',
    'version' => '2.0.0',
    'state' => 'stable',
    'constraints' => [
        'depends' => [
            'typo3' => '14.0.0-14.99.99',
            'php' => '8.2.0-8.5.99',
        ],
        'conflicts' => [],
        'suggests' => [],
    ],
];
// composer.json — TYPO3 v14
{
    "name": "vendor/my-extension",
    "type": "typo3-cms-extension",
    "require": {
        "php": "^8.2",
        "typo3/cms-core": "^14.0"
    },
    "extra": {
        "typo3/cms": {
            "extension-key": "my_extension"
        }
    }
}
Content Blocks: friendsoftypo3/content-blocks may require `typo3/cms-core` ^14.3 or higher (current 2.4.5 requires ^14.3) — match the Packagist constraint before pinning ^14.0 only.

2. PHP Requirements

Minimum PHP Version: 8.2

TYPO3 v14 requires PHP 8.2+. Use modern PHP features:

<?php
declare(strict_types=1);

namespace Vendor\Extension\Service;

// ✅ Constructor property promotion (PHP 8.0+)
final class MyService
{
    public function __construct(
        private readonly SomeDependency $dependency,
        private readonly AnotherService $anotherService,
    ) {}
}

// ✅ Named arguments (PHP 8.0+)
$result = $this->doSomething(
    name: 'value',
    options: ['key' => 'value'],
);

// ✅ Match expressions (PHP 8.0+)
$type = match ($input) {
    'a' => 'Type A',
    'b' => 'Type B',
    default => 'Unknown',
};

// ✅ Enums (PHP 8.1+)
enum Status: string
{
    case Draft = 'draft';
    case Published = 'published';
}

3. Controller Patterns (TYPO3 v14)

Extbase Action Controller

<?php
declare(strict_types=1);

namespace Vendor\Extension\Controller;

use Psr\Http\Message\ResponseInterface;
use TYPO3\CMS\Extbase\Mvc\Controller\ActionController;
use Vendor\Extension\Domain\Repository\ItemRepository;

final class ItemController extends ActionController
{
    public function __construct(
        private readonly ItemRepository $itemRepository,
    ) {}

    // ✅ Must return ResponseInterface (required in TYPO3 v14)
    public function listAction(): ResponseInterface
    {
        $items = $this->itemRepository->findAll();
        $this->view->assign('items', $items);
        return $this->htmlResponse();
    }

    public function showAction(int $item): ResponseInterface
    {
        $item = $this->itemRepository->findByUid($item);
        $this->view->assign('item', $item);
        return $this->htmlResponse();
    }

    // ✅ JSON response
    public function apiAction(): ResponseInterface
    {
        $data = ['success' => true, 'items' => []];
        return $this->jsonResponse(json_encode($data));
    }

    // ✅ Redirect
    public function createAction(): ResponseInterface
    {
        // Process creation...
        $this->addFlashMessage('Item created');
        return $this->redirect('list');
    }
}

Backend Module Controller

<?php
declare(strict_types=1);

namespace Vendor\Extension\Controller;

use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use TYPO3\CMS\Backend\Attribute\AsController;
use TYPO3\CMS\Backend\Template\ModuleTemplateFactory;

#[AsController]
final class BackendModuleController
{
    public function __construct(
        private readonly ModuleTemplateFactory $moduleTemplateFactory,
    ) {}

    public function indexAction(ServerRequestInterface $request): ResponseInterface
    {
        $moduleTemplate = $this->moduleTemplateFactory->create($request);
        $moduleTemplate->assign('items', []);
        
        return $moduleTemplate->renderResponse('Backend/Index');
    }
}

4. View & Templating (TYPO3 v14)

ViewFactory (Preferred Pattern)

<?php
declare(strict_types=1);

namespace Vendor\Extension\Service;

use Psr\Http\Message\ServerRequestInterface;
use TYPO3\CMS\Core\View\ViewFactoryData;
use TYPO3\CMS\Core\View\ViewFactoryInterface;

final class RenderingService
{
    public function __construct(
        private readonly ViewFactoryInterface $viewFactory,
    ) {}

    public function renderEmail(ServerRequestInterface $request, array $data): string
    {
        $viewFactoryData = new ViewFactoryData(
            templateRootPaths: ['EXT:my_extension/Resources/Private/Templates/Email'],
            partialRootPaths: ['EXT:my_extension/Resources/Private/Partials'],
            layoutRootPaths: ['EXT:my_extension/Resources/Private/Layouts'],
            request: $request,
        );
        
        $view = $this->viewFactory->create($viewFactoryData);
        $view->assignMultiple($data);
        
        return $view->render('Notification');
    }
}

Fluid Template Best Practices

<html xmlns:f="http://typo3.org/ns/TYPO3/CMS/Fluid/ViewHelpers"
      xmlns:core="http://typo3.org/ns/TYPO3/CMS/Core/ViewHelpers"
      data-namespace-typo3-fluid="true">

<f:layout name="Default" />

<f:section name="Main">
    <div class="content">
        <!-- ✅ Auto-escaped output -->
        <h1>{item.title}</h1>
        
        <!-- ✅ Explicit HTML (use with caution) -->
        <f:format.html>{item.bodytext}</f:format.html>
        
        <!-- ✅ Conditional rendering -->
        <f:if condition="{items}">
            <f:then>
                <f:for each="{items}" as="item">
                    <f:render partial="Item" arguments="{item: item}" />
                </f:for>
            </f:then>
            <f:else>
                <p>No items found.</p>
            </f:else>
        </f:if>
        
        <!-- ✅ Link building -->
        <f:link.action action="show" arguments="{item: item.uid}">
            View Details
        </f:link.action>
    </div>
</f:section>

</html>

5. Event System (TYPO3 v14)

PSR-14 Event Listeners

PSR-14 events are the standard in TYPO3 v14. Always prefer events over hooks.

<?php
declare(strict_types=1);

namespace Vendor\Extension\EventListener;

use TYPO3\CMS\Core\Attribute\AsEventListener;
use TYPO3\CMS\Frontend\Event\ModifyCacheLifetimeForPageEvent;

#[AsEventListener(identifier: 'vendor-extension/modify-cache-lifetime')]
final class ModifyCacheLifetimeListener
{
    public function __invoke(ModifyCacheLifetimeForPageEvent $event): void
    {
        // Reduce cache lifetime for certain pages
        if ($event->getPageId() === 123) {
            $event->setCacheLifetime(300); // 5 minutes
        }
    }
}

Common Events (TYPO3 v14)

EventPurpose
ModifyPageLinkConfigurationEventModify link building
ModifyCacheLifetimeForPageEventAdjust page cache
BeforeStdWrapFunctionsInitializedEventModify stdWrap
(DataHandler)Core ships no generic BeforeRecordOperationEvent / AfterRecordOperationEvent; use `SC_OPTIONS` DataHandler hooks or documented TYPO3\CMS\Core\DataHandling\Event\* only (list)

Services.yaml Registration

# Configuration/Services.yaml
services:
  _defaults:
    autowire: true
    autoconfigure: true
    public: false

  Vendor\Extension\:
    resource: '../Classes/*'
    exclude:
      - '../Classes/Domain/Model/*'

  # Event listener (alternative to #[AsEventListener] attribute)
  Vendor\Extension\EventListener\MyListener:
    tags:
      - name: event.listener
        identifier: 'vendor-extension/my-listener'

Detailed Reference

Read the full guide when the task needs detailed examples, long templates, troubleshooting matrices, appendices, or sections not included above. Keep this file unloaded for narrow tasks so the skill follows progressive disclosure.

Related skills

This week in AI coding

Five minutes, every Monday - the tools, releases and tactics for developers.

unsubscribe anytime.