TYPO3 extensions

TYPO3 extensions: build, adapt, keep them ready for new versions

Three situations bring people to this page. Your TYPO3 website needs a function the core does not provide. An existing extension should behave differently. Or an extension is standing in the way of the upgrade to v13 or v14. Sharpness Solutions GmbH in Oldenburg, Germany, has worked with TYPO3 since 2004. We build extensions as Composer packages against TYPO3's intended extension points, connect third-party systems, update other people's extensions for new versions and take over custom code whose author has gone. TYPO3 v14 has been the current long-term support (LTS) version since 21 April 2026; v13, the previous LTS, still receives security updates. Last updated: 31 October 2026. Telephone +49 441 21 21 63 0, Mon–Thu 9:00–16:00, Fri 9:00–13:00 (CET). The first question is always whether anything needs building at all.

Make an enquiry 0441 21 21 63 0 Mon – Thu 9:00 – 16:00, Fri 9:00 – 13:00 (CET)

Where TYPO3 extensions become an issue

Enquiries about TYPO3 extensions rarely arrive with a specification. They arrive with the error message from a failed upgrade, with a list of extensions nobody can explain any more, or with the sentence “surely TYPO3 can do that”. Six starting points come up again and again. What they have in common is that the real decision is made before the first line of code: core, maintained extension or custom development. Skip that decision and you pay for it at the next major release.

Three extensions, none quite fits

The TER (TYPO3 Extension Repository) and Packagist offer several candidates. Each covers part of the requirement, one has not seen a release in years, another brings twenty options for cases you do not have. Combining them means bringing several outside maintenance cycles into your project. Before we build anything, we check whether one candidate is enough with an adaptation — or whether the core has been able to do it for a while.

An extension is blocking the upgrade

Either Composer stops because the extension's constraint does not allow typo3/cms-core in version 14, or the upgrade runs through and content elements that used to show a plugin now output nothing. In v14 the most common cause is the removal of plugin registration via list_type. A relaxed constraint does not fix that: the extension needs its own, adapted release.

The author no longer answers

The extension works, but its last version dates from the v11 era, and questions about v13 and v14 sit unanswered in the issue tracker. The function lives in your website; decisions about its future are made by somebody else. The way out is replacement, a fork or a rebuild. The GPL allows the fork — the only open question is who maintains it afterwards.

Nobody knows the old custom extension

A previous agency built an extension that does central work. It sits in typo3conf/ext with no repository, no documentation and no tests. It runs, but nobody dares touch it, and every upgrade quote carries a hefty risk margin at exactly this point. That only changes once somebody has read the code.

Data from another system should appear on the website

Vehicle stock, product data, job vacancies or events live in another system and are retyped by hand or uploaded as a file for the website. That costs time and produces two versions that never quite match. An interface extension solves this — provided it has been settled beforehand which system owns which field.

Somebody patched the core

To change a behaviour, somebody edited a core file or replaced a class via XCLASS. That works until the next update. After that, the patch has quietly disappeared, or the replaced class no longer fits the rest. An intervention that blocks updates is a design fault, not fate. It can be repaired — just not as a side task on upgrade day.

First check whether anything needs building

The first look goes into the core, and it pays off more often than people expect. Out of the box, TYPO3 provides forms through the Form Framework, redirect management, categories, multilingual sites through the site configuration, a full-text search and the Scheduler for recurring tasks. Since v13, site sets bundle TypoScript, settings and dependencies into packages that can be activated per site. Much of what used to justify a small extension is now a few lines of configuration or an adjusted Fluid template. So before we write an extension, we check whether TypoScript, a site set or the core already solve the task. An extension that is never installed costs nothing — neither to build nor at the next update.

The second route is a maintained extension from the TER or Packagist. For news, calendars, maps, cookie consent or search integration there are established solutions, and building your own alongside them would be a waste. “Maintained” means something specific: a release for the current LTS, a maintainer who responds to bug reports, a public issue tracker and a record of security fixes that arrived promptly. It also matters how an extension handles new major versions: from version 14.0.0, the news extension no longer supports TYPO3 v12 — anyone who wants to keep it current has to move with it. If an author does not deliver a fix for a security issue, the TYPO3 Security Team removes the affected versions from the TER. If a maintained extension covers your requirement, we recommend it, set it up and include it in maintenance rather than building the same function a second time.

The third route is custom development. It delivers exactly the function your process needs, without configuration screens for cases you do not have and without somebody else's roadmap. In return, maintenance is part of the deal from day one. Every extension, downloaded or built, is an item in your running costs: every core patch update has to check it, every major upgrade requires it to be adapted, every security notice about it has to be assessed. That is why we compare the routes over the years you intend to run the website, not over the quoted price. And some requirements are not extension requirements at all but an editorial rule nobody has written down yet. Those get written down, not programmed.

How we build an extension

We build an extension as a Composer package with its own vendor namespace and a composer.json that states the type typo3-cms-extension, the extension key and the supported TYPO3 versions. From v14 onwards a valid composer.json is mandatory anyway, including in classic-mode installations. Project-specific extensions live in the project repository, reusable ones in a repository of their own; you receive the source code in a Git repository that belongs to you. Configuration lives in files and therefore in version control: TCA for tables and fields, a site set for TypoScript and settings, Symfony dependency injection for the services. What editors may change sits in the backend. What is code sits in the repository — and nowhere else.

We use Extbase and Fluid where they fit: records with list and detail views, forms with validation, a domain model that is more than a single table. For a JSON endpoint, a teaser fed from an external API or a small piece of output, Extbase is overhead; a lean controller or a PSR-15 middleware is enough. We build custom content elements with Content Blocks — an extension, not part of the core — or with our own TCA definitions and a Fluid template, so editors can see what they are maintaining. At Eschen Nutzfahrzeuge (used trucks and commercial vehicles), the vehicle stock is set up as structured records, maintained by the client's own editors and usable on mobile. Structured records can be filtered, sorted and reused elsewhere; running text cannot. Anything that fits into no content element — an import overview, an approval list — gets its own backend module.

We hook in through the extension points TYPO3 provides. PSR-14 events to attach to processes in the core and in other extensions. PSR-15 middlewares for anything that happens to the request or the response, such as a custom API endpoint. Symfony dependency injection to configure services rather than creating them with new. TCA overrides to add fields to existing tables. And the DataHandler whenever records are written programmatically — then permissions, logging, the reference index and the cache work just as they do for input in the backend. What does not belong: core patches, which the next Composer update overwrites, and XCLASS, which replaces a core class entirely; only one XCLASS per class takes effect, and every change to the original has to be followed by hand. Whatever hooks in through an intended extension point generally survives a major release. Whatever goes around it has an expiry date.

Interface extensions: data from other systems

Many extensions have a single job: bringing data from another system onto the website. At Autohaus Brüggemann, a car dealership with six locations from Rheine to Rostock, eight franchised brands and stock in the four-digit range, the TYPO3 website brings all vehicles together in one shared search. It is fed by the dealer management system, not by the editorial team. That is the pattern: whatever another system already holds is not maintained a second time by hand. The website shows the same state as the system behind it, and nobody retypes vehicle data. For that to hold, it has to be settled beforehand which data flows in which direction, how often, and what happens when something fails.

Technically, we run an import as a Symfony Console command, callable via vendor/bin/typo3 and scheduled through the Scheduler. That means it can be triggered by hand, tested and traced on the command line instead of running inside a frontend request that times out on large data volumes. Every run writes a log: how many records were created, changed or deleted, which were rejected and why. An interrupted run can be repeated without damage, because each record is matched by a unique key from the source system rather than by its position in a file. Data is written through the DataHandler or into a clearly separated table of its own — never by direct access to core tables that bypasses permissions and caching.

The decisive question is not a technical one: which system owns which field? If price and availability come from the source system, editors must not overwrite them — otherwise either the website is wrong after the next import or the editors' work is lost. Additional texts, images or highlights, on the other hand, stay editorial, in separate fields the import never touches. We put this ownership per field in writing before the first line of code. We also draw one boundary deliberately: a shop does not belong in the CMS as an extension. At M&S Armaturen (industrial valves), the corporate website runs on TYPO3 and the Shopware shop is connected to it. A sales process with pricing rules, payment methods and returns belongs in a shop system, and we build that alongside with Shopware 6 instead of forcing it into a CMS extension.

Third-party and orphaned extensions in an upgrade

In a major upgrade, the brake is rarely the core and usually the extension list. That is why every upgrade we carry out starts with an inventory: which extensions are installed, which of them are actually used, who maintains them, is there a release for the target version? Whether an extension is used is shown by the database, not by anybody's memory — a plugin that is no longer placed on any page does not need migrating; it needs removing. In the end every extension gets a note: update, replace with the core, replace with a maintained extension, port, rebuild or remove. This list is usually shorter than feared, and it is the basis of any honest estimate.

For an extension without a release for the target version there are three routes. Replacement with the core or a maintained alternative, even if configuration and data have to move in the process. Porting, meaning taking the code into your own repository and adapting it to the new version — the GPL under which TYPO3 extensions are published allows this, but with the fork comes the maintenance. Or rebuilding the function, often with a smaller scope, because it turns out that half the options were never used. A rebuild has an advantage that is often overlooked: what the function has to do can be read off the running system. Both figures, porting and rebuild, sit side by side in the estimate, even when one of them is the smaller job.

For the porting itself there are tools that take part of the work off your hands. The Extension Scanner in the Install Tool finds calls to API that has been removed or deprecated in the target version. TYPO3 Rector rewrites part of those places automatically, and Fractor does the same for TypoScript, FlexForms, YAML and Fluid. What remains is manual work: business logic in templates, dependencies with conflicting Composer constraints and, in v14 above all, switching plugin registration from list_type to dedicated CTypes, including an upgrade wizard so that existing content elements keep rendering after the upgrade. Which breaking changes v14 brings in detail, and how an upgrade project runs, is covered on our TYPO3 update page. Here the subject is the code that has to move along with it.

Taking over somebody else's custom extensions

We take over custom extensions built by a previous agency, but not blindly. Before we commit, there is a code review with the same set of questions every time. Is there a composer.json, and which TYPO3 version is it tied to? Does the code live in a repository, or only on the server? Does the extension hook in through events and dependency injection, or through XCLASS, outdated hooks and direct access to core tables? Does it write records through the DataHandler? Is business logic sitting in Fluid templates or in TypoScript? What does the Extension Scanner find against the target version? Do tests exist, and do they pass? This is work of hours to days, and a defined first deliverable with its own price, which we state in writing beforehand.

The result is a findings report: what the extension does, where it reaches into core internals and what needs doing for the next version, with a prioritised risk list and the effort in person-days. Next to it sits the alternative of a rebuild with its own figure, so you can choose between the two routes before money goes into the code. And we decline if an extension is built in a way that means we cannot vouch for its behaviour after the next update, or if central parts were delivered without source code — then a rebuild is the more honest job. Taking over somebody else's code without reading it first mostly means taking over its surprises.

After the takeover comes order first, then maintenance. The code moves into a Git repository that belongs to you and gets a composer.json, version numbers following semantic versioning and documentation explaining what the extension does and how it is configured. Critical processes get tests with the TYPO3 Testing Framework before anyone changes them — otherwise nobody knows whether a change broke something. Only then do we port or extend. Ongoing maintenance runs under our TYPO3 maintenance with a service level agreement, and if you wish together with hosting on our own servers in the Hetzner data centre in Falkenstein, Germany. Like TYPO3 itself, the extension is licensed under the GPL; the source code belongs to you, and the domain is registered in your name.

Process

Check first, then code.

If a function is missing, describe the process it should replace. If an extension is blocking the upgrade, the output of composer show or the list of installed extensions is enough. You get a written assessment with a recommended route, an effort range and the alternative next to it — even if the core or a maintained extension does the job and all we do is set it up. Sharpness Solutions GmbH, Edewechter Landstraße 161, Oldenburg, Germany. Phone +49 441 21 21 63 0, Mon–Thu 9:00–16:00, Fri 9:00–13:00 (CET). We work remotely across Germany, with meetings in the north-west by arrangement.

  1. 01

    Clarifying the requirement

    First we clarify what should happen and where — not which technology to use. Who does the task today, and how? Which data comes from where? Does the rule to be automated apply without exception? A rule with unnamed exceptions does not become clearer in code, only more expensive.

  2. 02

    Checking core, TER and Packagist

    We check whether the core, a site set or TypoScript already solve the task, and review the TER and Packagist for maintained extensions. For each candidate we look at the release for the current LTS, the state of maintenance and the extension points. In parallel we establish which events and APIs TYPO3 offers for your requirement. The result is a short list of realistic routes.

  3. 03

    Decision and scope

    Use, adapt or build — and what belongs in the extension, what in the configuration and what remains editorial work. You get an effort estimate in person-days with separate items, a fixed price for clearly defined parts and a named list of what can only be decided on the test system.

  4. 04

    Development in your repository

    The work happens in a Git repository that belongs to you, against a staging instance, never on the live system. The extension hooks in through events, middlewares, dependency injection and TCA, not through XCLASS or core patches. Tests and static analysis run with every commit.

  5. 05

    Acceptance and deployment

    You test on the staging system against an acceptance list. Before release there is an update test: installation, database comparison and a core patch update with the extension active. Deployment follows the agreed route, with a backup immediately beforehand and a documented way back.

  6. 06

    Maintenance across versions

    From then on the extension moves with every release. Before each major upgrade the Extension Scanner runs against the target version, so adaptations arrive before they become urgent. Maintenance runs under our TYPO3 maintenance with a service level agreement: 24, 8 or 4 hours response time, as a monthly flat fee, net, and if you wish with hosting on our servers in Germany.

Projects on this system

TYPO3 M&S Armaturen Multilingual TYPO3 site with a direct link into the shop
TYPO3 · Fluid · Bootstrap · WCAG 2.1 AA · EN 301 549 J. Bünting Beteiligungs AG Operable without a mouse — contrast and text size on every page.

A selection — not the full client list.

Across the north-west

From Oldenburg — from Wuppertal to Hamburg

We work for companies from Wuppertal and Münster via the Emsland and East Frisia to Hamburg and Hanover. Meetings on site by arrangement, the ongoing work remotely. Each region has a page of its own.

Oldenburg and surroundings

Bremen and surroundings

East Frisia and the coast

Emsland and County of Bentheim

Oldenburg Münsterland and Osnabrück

Münsterland

Hanover and East Westphalia

Ruhr and Bergisches Land

Frequently asked questions

What does developing a TYPO3 extension cost?

A TYPO3 extension is priced by effort, in person-days with separate items; there are no package prices. The effort depends on four factors: how many custom data structures and content elements are needed, whether a backend module is part of it, whether a third-party system is connected and how many TYPO3 versions the extension has to support. An extension that provides one content element with three fields is a different item from an import with logging and ownership defined per field. A reliable figure exists once the requirement has been clarified; clearly defined parts we offer at a fixed price. Our hourly rate is available on request.

Isn't there a ready-made extension for this already?

Often there is, and then we recommend it rather than building the same function again. Before we develop anything, we check whether the core, a site set or TypoScript already solve the task, and then review the TER and Packagist. A third-party extension is fit for use if it has a release for the current LTS, a maintainer responds to bug reports and security fixes have arrived promptly in the past. Part of this is a sober calculation: every installed extension has to be checked at every update. Three extensions that half fit are therefore often more expensive than one of your own that fits exactly.

How is an extension made ready for TYPO3 v14?

With its own release, not with a relaxed Composer constraint. First the Extension Scanner in the Install Tool finds calls to removed or deprecated API, then TYPO3 Rector rewrites part of them automatically; Fractor does the same for TypoScript, FlexForms and Fluid. The rest is manual work. In v14 this mainly affects plugins still registered via list_type: they are registered as their own CType and get an upgrade wizard so that existing content elements keep rendering. A valid composer.json is also mandatory. The breaking changes in detail are covered on our TYPO3 update page.

Can an extension support TYPO3 v13 and v14 at the same time?

Often, yes: an extension can allow both LTS versions in its Composer constraint, as long as it only uses API that exists in both. Where that is not possible, it needs two release lines with separate branches and separate tests — which costs closer to a major step than to a bug fix. Whether that is worthwhile depends on where the extension runs. If it only runs on your own website, one line for the version you operate is enough. TYPO3 v13 receives free security updates until 31 December 2027; by then at the latest the second line has served its purpose.

What can we do if an extension is no longer maintained?

There are three routes: replacement with the core or a maintained alternative, taking over the code as a fork in your own repository, or rebuilding the function. Legally, a fork is rarely the problem with TYPO3 extensions, because they are licensed under the GPL. With the fork, however, release responsibility moves to you, and with it the maintenance, which we take on as part of our maintenance service. Which route is cheaper depends on the functionality you actually use. The inventory often shows that half the options were never used; then a rebuild is smaller than the old extension suggests.

Do you take over an extension that another agency developed?

Yes, after a code review. We look at composer.json and the version constraint, whether the code lives in a repository, how the extension hooks in — events and dependency injection, or XCLASS, outdated hooks and direct access to core tables —, whether records are written through the DataHandler, whether business logic sits in templates and whether tests exist. Plus a run of the Extension Scanner against the target version. The result is a findings report with the effort and the alternative of a rebuild next to it. Documentation and ongoing maintenance follow. Sometimes we decline, for example when central parts were delivered without source code.

Can you adapt an extension from the TER without changing it?

Yes, in most cases through a small companion extension of our own. It attaches to the PSR-14 events of the third-party extension, reconfigures its services via dependency injection, adds fields through TCA overrides and overrides individual Fluid templates via additional template paths in TypoScript. The third-party extension stays unchanged and can still be updated. We check the limits beforehand: if an extension offers no events and encapsulates its logic in classes that cannot be swapped, we say so before you commission the work — then a fork or a rebuild is the more honest route.

How does data from our other systems get into TYPO3?

Through an interface extension that imports the data on a schedule and stores it as structured records. With us, such an import runs as a Symfony Console command started by the Scheduler, with a log for every run and a unique key per record, so that an interrupted run can be repeated without damage. Before that, we define which system owns which field. This is how the TYPO3 website of Autohaus Brüggemann brings all vehicles from six locations together in one shared search, fed by the dealer management system rather than by the editorial team.

Why not simply patch the core or replace a class via XCLASS?

Because both put the next update at risk. A patch to a core file is overwritten by the next Composer update, often without anybody noticing. XCLASS replaces a core class entirely; every change TYPO3 makes to the original has to be followed by hand, and only one XCLASS per class takes effect. For almost every requirement there is an intended route: PSR-14 events, PSR-15 middlewares, Symfony dependency injection, TCA overrides and the DataHandler. XCLASS remains the last resort when none of these works — and then documented, with a date for removing it again.

Can we run an online shop with TYPO3?

We do not build a shop as a TYPO3 extension; we connect the TYPO3 website to Shopware 6 instead. Shop extensions for TYPO3 exist, but a sales process with pricing rules, payment methods, returns and an ERP connection belongs in a shop system. Content, page structure and editing stay in TYPO3, while basket and order run in Shopware. That is how it works at M&S Armaturen: the corporate website runs on TYPO3, multilingual and maintained by the client's own editors, and the Shopware shop is connected to it. An extension that rebuilds a shop would turn every TYPO3 upgrade into a project of its own.

Who owns the code, and under which licence is it published?

The source code belongs to you and, like TYPO3 itself, is licensed under the GPL. We develop in a Git repository that belongs to you; domain, licences and accounts are registered to your organisation. The GPL does not oblige you to publish an extension that only runs on your own website — it governs what applies when the extension is passed on. Whether a custom extension is released as open source in the TER is your decision. For an extension that serves exactly one website, that rarely brings anything except extra effort for third-party requests.

Who maintains the extension after acceptance?

We do, as part of our TYPO3 maintenance with a service level agreement. The tiers range from 24 through 8 to 4 hours response time, as a monthly flat fee, net; on-call cover outside business hours is only available with an SLA. Without an SLA we handle requests within 48 hours during business hours, Mon–Thu 9:00–16:00, Fri 9:00–13:00 (CET). Before every major upgrade we check the extension against the target version with the Extension Scanner, so that adaptations are planned rather than made on update day. If you wish, we also host the whole installation on our own servers in Germany.

Enquiry

Which function is missing — or which extension is holding you back?

Describe briefly what the extension should do, or which existing one is in your way. Whether the core or a maintained extension can already do it is something we check as well — and if it can, we say so.

  • An answer from someone who knows the system — no phone queue
  • An assessment before the quote, even when it advises against the project
  • Your details are sent to us by email, not into a third-party CRM

Spam protection: Cloudflare Turnstile — no cookies, no tracking.

Call Start a project