TypoScript Conditions Cheatsheet: Syntax, Examples and Migration

Improve your TYPO3 website's functionality with modern TypoScript conditions! Learn guidelines and examples with this easy-to-use cheat sheet .

TypoScript Conditions Cheatsheet: Syntax, Examples and Migration

Modern TypoScript conditions help you write cleaner, safer, and more maintainable TYPO3 configuration. This ultimate guide is built as a practical cheatsheet, focused on real-world examples using Symfony Expression Language for fast copy-paste and immediate use.

Since TYPO3 v9.4, Symfony Expression Language powers modern TypoScript conditions and replaces legacy syntax. If you work with TYPO3 daily, you already know how critical conditions are for controlling rendering, environments, and behavior across projects.

Instead of theory, this guide focuses on how modern TypoScript conditions actually work, including variables, functions, custom conditions, and common pitfalls. 

In this guide, we will walk through everything you need to use TypoScript conditions correctly, confidently, and upgrade-safe.

Short answer: A TypoScript condition is a Symfony Expression Language expression in square brackets, such as [frontend.user.isLoggedIn]. The TypoScript below it applies only while the expression is true, until [ELSE], [END] or [GLOBAL]. Conditions read variables such as page and tree and functions such as site(), and work in TYPO3 9.4 and newer.

Key takeaways

  • The legacy syntax such as [globalVar = ...] was deprecated in TYPO3 9.5 and removed in TYPO3 10.
  • getTSFE() was removed in TYPO3 v14. Read the page ID with request?.getPageArguments()?.getPageId().
  • loginUser() and usergroup() were deprecated in v12 and removed in v13. Use frontend.user and backend.user.
  • Since TYPO3 v12, [END] and [GLOBAL] behave the same, and conditions can only be nested across separate files, for example with @import.
  • TYPO3 v14 adds locale() as a shortcut to the site locale. ip() works only in frontend TypoScript since v13.
  • Guard request checks with request && or the null-safe operator ?. so they cannot fail in CLI or TSconfig.

Tested with / valid for: TYPO3 v13.4 LTS and v14.3 LTS, checked against the official TypoScript reference on conditions and the TYPO3 Core changelog.
Updated: November 2026

Each condition below is copy-paste ready for frontend TypoScript setup. The "Works in" column names the first TYPO3 version that supports the syntax; every row was checked against the TypoScript reference for v13.4 and v14. FE means frontend TypoScript, BE means backend TSconfig.

Syntax in one block

 

[frontend.user.isLoggedIn]
page.10.value = Welcome back
[ELSE]
page.10.value = Please log in
[END]

 

Combine checks with && or and, || or or, and negate with !. Since TYPO3 v12, [END] and [GLOBAL] do the same thing (syntax reference).

Page and page tree

Use caseTypoScript conditionContextWorks in
Current page UID[traverse(page, "uid") == 17]FEv9.4+
Several pages[traverse(page, "uid") in [17,24]]FEv9.4+
Page UID range[traverse(page, "uid") in 10..20]FEv13.4, v14
Page ID from routing (replaces getTSFE().id)[request?.getPageArguments()?.getPageId() == 42]FEv12.1+
Page type, for example 98[request && request.getPageArguments()?.getPageType() == 98]FEv12.1+
Backend layout with inheritance[tree.pagelayout === "pagets__Home"]FEv13.4, v14
Root level page[tree.level == 0]FEv9.4+
Page 2 is in the rootline[2 in tree.rootLineIds]FEv9.4+
Any subpage of page 30[30 in tree.rootLineParentIds]FEv10.3+

Site and language

Use caseTypoScript conditionContextWorks in
Language ID[siteLanguage("languageId") == 1]FEv9.4+
Locale of the site language[siteLanguage("locale").getName() == "de-DE"]FEv13.4, v14
Locale shortcut[locale().getName() == "de-DE"]FEv14
Site identifier[site("identifier") == "main"]FEv9.4+
Host of the site base[site("base").getHost() == "www.example.org"]FEv13.4, v14
Custom site setting[traverse(site("configuration"), "settings/some/setting") == "someValue"]FEv13.4, v14

Frontend and backend users

Use caseTypoScript conditionContextWorks in
Frontend user logged in[frontend.user.isLoggedIn]FEv9.4+
Frontend user not logged in[!frontend.user.isLoggedIn]FEv9.4+
Frontend user in group 11[11 in frontend.user.userGroupIds]FEv11.2+
Backend user logged in[backend.user.isLoggedIn]FE / BEv9.4+
Backend user is admin[backend.user.isAdmin]FE / BEv9.4+
Backend user in group 2[2 in backend.user.userGroupIds]FE / BEv11.2+

Request: host, HTTPS, parameters, device

Use caseTypoScript conditionContextWorks in
HTTPS request[request && request.getNormalizedParams().isHttps()]FEv9.4+
Exact host[request && request.getNormalizedParams().getHttpHost() == "example.org"]FEv9.4+
Any subdomain[request && like(request.getNormalizedParams().getHttpHost(), "*.example.org")]FEv9.4+
GET parameter tx_news_pi1[news][request && traverse(request.getQueryParams(), 'tx_news_pi1/news') > 0]FEv9.4+
Cookie value[request && request.getCookieParams()['foo'] == 1]FEv9.4+
User agent contains "Mobile"[request && like(traverse(request.getHeaders(), 'user-agent/0'), '*Mobile*')]FEv13.4, v14

Context, version, IP and date

Use caseTypoScript conditionContextWorks in
Development context[applicationContext == "Development"]FE / BEv9.4+
Production and its sub-contexts[applicationContext matches "/^Production/"]FE / BEv9.4+
TYPO3 13.4 or newer[compatVersion("13.4")]FE / BEv9.4+
Exact TYPO3 branch[typo3.branch == "14.3"]FE / BEv9.4+
Client IP matches devIPmask[ip("devIP")]FEv9.4+, FE only since v13
Feature toggle enabled[feature("security.frontend.enforceContentSecurityPolicy") === true]FE / BEv13.4, v14
Friday[date("w") == 5]FE / BEv9.4+
Between 9:00 and 16:59[date("G") >= 9 && date("G") < 17]FE / BEv9.4+
Live workspace[workspace.workspaceId == 0]FE / BEv9.4+
Environment variable[getenv("VIRTUAL_HOST") == "www.example.org"]FE / BEv9.4+

Note: compatVersion("13.4") is also true on TYPO3 14, because it checks for "this version or newer". User agent checks are a rough signal; responsive CSS is the more reliable way to adapt layouts to devices.

Symfony Expression Language conditions work in every TYPO3 version from 9.4 on, including the current v13.4 LTS and v14.3 LTS. What changes between versions is the set of variables and functions you can call.

Which TYPO3 versions support Symfony TypoScript conditions?

TYPO3 versionExpression syntaxWhat changed for conditions
9.3 and olderNoLegacy syntax only
9.4 / 9.5 LTSYesSymfony Expression Language added in 9.4, legacy syntax deprecated in 9.5
10 LTSYesLegacy conditions removed; tree.rootLineParentIds added in 10.3
11 LTSYesuserGroupIds arrays for frontend and backend users added in 11.2
12 LTSYesNew TypoScript parser: [END] equals [GLOBAL], @import inside conditions, null-safe ?. (12.1); loginUser() and usergroup() deprecated
13.4 LTSYesloginUser() and usergroup() removed, ip() frontend-only, <INCLUDE_TYPOSCRIPT: deprecated
14.3 LTSYesgetTSFE() removed, locale() added, <INCLUDE_TYPOSCRIPT: removed

Support status as of November 2026: TYPO3 v14.3.0 LTS was released on 21 April 2026. Free community support for TYPO3 v12 LTS ended on 30 April 2026, and v13 LTS receives security updates until the end of December 2027, according to the TYPO3 announcement on v12 end of free support.

Deprecated vs Recommended Condition Syntax

  • Legacy conditions ([globalVar], [userFunc]) were deprecated in TYPO3 9.5 and removed in TYPO3 10
  • Symfony-based conditions are:
    • Forward-compatible
    • Upgrade-safe
    • Actively maintained

Use only Symfony Expression Language for new and updated projects.

Migration & Upgrade Guidance

  • TYPO3 ≤ 9.3
    Rewrite all conditions as part of the upgrade
  • TYPO3 12 to 13.4
    Replace loginUser() and usergroup()
  • TYPO3 13.4 to 14
    Replace every getTSFE() call and every <INCLUDE_TYPOSCRIPT: line

Enterprise Best Practices

  • Validate conditions on every upgrade, for example from TYPO3 12 LTS to 13.4 LTS and 14.3 LTS
  • Never mix legacy and modern condition syntaxes
  • For the wider upgrade steps, follow our TYPO3 v13 migration guide

TYPO3 integrates selected Symfony components to strengthen its core architecture without adding unnecessary complexity.

  • The components, such as Console, Routing, YAML, PropertyAccess, and ExpressionLanguage, are deeply embedded in the TYPO3 Core and designed for long-term stability.
  • Symfony Expression Language provides a compact, readable way to evaluate logical expressions that return a value, most commonly booleans.
  • TYPO3 uses this component to power modern TypoScript conditions, replacing fragmented legacy syntax with a unified expression model.
  • Since TYPO3 v9.4, Symfony Expression Language is the foundation for TypoScript conditions in both Frontend and Backend contexts.

Condition logic is exposed through variables and functions, enabling cleaner configuration, easier extension, and fully upgrade-safe custom conditions aligned with TYPO3’s long-term roadmap.

TYPO3 replaced legacy TypoScript conditions with Symfony Expression Language to introduce a unified, extensible, and consistent syntax.

Below is a direct before - after comparison, showing how common conditions translate from legacy syntax to modern TYPO3 (≥ 9.4).

Migrating old conditions: quick table

The first eleven rows are legacy conditions that TYPO3 10 removed. The last three are expression functions that stopped working in TYPO3 v13 or v14. Sources: the deprecation of the old syntax, the loginUser() and usergroup() deprecation and the getTSFE() removal.

Old conditionReplacementNote
[globalVar = GP:L = 1][siteLanguage("languageId") == 1]Language comes from the site configuration
[globalVar = TSFE:id = 17][traverse(page, "uid") == 17]
[treeLevel = 0][tree.level == 0]
[PIDinRootline = 2][2 in tree.rootLineIds]
[PIDupinRootline = 30][30 in tree.rootLineParentIds]Since TYPO3 10.3
[loginUser = *][frontend.user.isLoggedIn]
[usergroup = 11][11 in frontend.user.userGroupIds]Since TYPO3 11.2
[applicationContext = Development*][applicationContext matches "/^Development/"]
[globalString = IENV:HTTP_HOST = *.example.com][request && like(request.getNormalizedParams().getHttpHost(), "*.example.com")]
[dayofweek = 5][date("w") == 5]
[IP = devIP][ip("devIP")]Frontend only since v13
[getTSFE().id == 42][request?.getPageArguments()?.getPageId() == 42]getTSFE() removed in v14
[loginUser('*')][frontend.user.isLoggedIn]Removed in v13
[usergroup(11)][11 in frontend.user.userGroupIds]Removed in v13

Condition Syntax Structure

TYPO3 ≤ 9.3 (Legacy)

Conditions were evaluated individually and chained together.

 

[condition1] && [condition2]

 

TYPO3 ≥ 9.4 (Symfony-based)

Conditions are evaluated as a single expression.

 

[condition1 && condition2]

 

Language Detection

Legacy

 

[globalVar = GP:L = 1]

 

Modern

 

[siteLanguage("languageId") == 1]

[siteLanguage("title") == "English"]

[siteLanguage("locale").getName() == "en-US"]

 

TypoScript Constants

Legacy

 

[globalVar = LIT:1 = {$myConstant}]

 

Modern

 

[{$myConstant} == 1]

 

Page UID Checks

Legacy

 

[globalVar = TSFE:id = 17]

[globalVar = TSFE:id = 17, TSFE:id = 24]

 

Modern

 

[page["uid"] == 17]

[page["uid"] in [17,24]]

[request?.getPageArguments()?.getPageId() == 17]

 

Older examples use getTSFE().id. That function was removed in TYPO3 v14, so use the page variable or the page arguments shown above.

Page Properties

Legacy

 

[page|backend_layout = 1]

 

Modern

 

[page["backend_layout"] == 1]

 

Domain / Hostname Checks

Legacy

 

[globalString = IENV:HTTP_HOST = *.example.com]

 

Modern

 

[like(request.getNormalizedParams().getHttpHost(),"*.example.com")]

 

Date-Based Conditions

Legacy

 

[dayofweek = 5]

 

Modern

 

[date("w") == 5]

 

Extension / Request Parameters

Legacy

 

[globalVar = GP:tx_extkey|param > 0]

 

Modern

 

[request && traverse(request.getQueryParams(), 'tx_extkey/param') > 0]

 

Why the New Syntax Is Better

  • One unified expression language
  • Cleaner, more readable conditions
  • Supports complex logic (&&, ||, in, regex)
  • Enables custom TypoScript conditions
  • Consistent behavior across Frontend and Backend

Free guide: Upgrade Playbook for TYPO3

Removed functions such as getTSFE() belong on every upgrade checklist. The free Upgrade Playbook plans your TYPO3 upgrade in four phases, with ELTS facts, a maintenance calendar and an Excel worksheet.

Get the free Upgrade Playbook

Modern TypoScript conditions expose variables and functions through Symfony Expression Language. Both are used inside condition expressions, but they serve different purposes.

Variables

Variables represent available runtime data and are used for direct comparisons.

Characteristics

  • Read-only
  • No arguments
  • Context-dependent (FE / BE)

Typical use cases

  • Page properties
  • Language information
  • User state
  • Application context
  • Site configuration

Example:

 

[page["uid"] == 1]

 

Functions

Functions encapsulate logic or processing and may accept parameters.

Characteristics

  • Callable expressions
  • Accept arguments
  • Can return dynamic values

Typical use cases

  • Request inspection
  • Date evaluation
  • Pattern matching
  • Version checks
  • Environment logic

Example:

 

[date("w") == 5]

 

Use variables for simple checks and functions for dynamic or computed conditions. The sections below are grouped accordingly.

Why This Distinction Matters

  • Prevents misuse of conditions
  • Improves readability in complex setups
  • Helps structure custom condition providers
  • Makes debugging faster

TYPO3 provides a set of built-in variables that can be used directly in modern TypoScript conditions. These variables expose runtime information and are evaluated using Symfony Expression Language.

Each variable below represents a data source, not executable logic.

applicationContext - Application Environment

Returns the current TYPO3 application context as a string.

Common use cases

  • Environment-based configuration
  • Feature toggles per stage (Dev / Staging / Live)
[applicationContext == "Development"]
[applicationContext matches "#^Development#"]
[applicationContext matches "#^Production/Dev#"]
[applicationContext matches "#^Production/(Dev|Staging)#"]
[applicationContext matches "#^Production/Live#"]
[applicationContext == "Production/Live/ClusterServer1"]
[applicationContext matches "#^Production/Live#" && backend.user.isLoggedIn]
[not (applicationContext matches "#Production#")]

 

page - Page Properties

Provides access to the current page record as an array.

Common use cases

  • Page-based rendering logic
  • Template or layout switching
[page["pid"] == 2]
[page["uid"] == 17]
[page["backend_layout"] == 1]

 

TypoScript Constants - {$foo.bar}

All TypoScript constants are available inside conditions. Be mindful of data types when comparing values.

 

[{$foo.bar} == 4711]
["{$foo.bar}" == "4711"]

 

tree - Page Tree Information

Provides access to page tree and rootline data.

Common use cases

  • Root page detection
  • Section-based configuration
[tree.level == 0]
[tree.rootLine[0]["uid"] == 1]
[2 in tree.rootLineIds]
[30 in tree.rootLineParentIds]
[tree.pagelayout === "pagets__Home"]

 

backend - Backend User Context (FE and BE)

Exposes backend user information. Available in frontend TypoScript and in backend TSconfig.

 

[backend.user.isAdmin]
[backend.user.isLoggedIn]
[backend.user.userId == 5]
[2 in backend.user.userGroupIds]

 

frontend - Frontend User Context (FE Only)

Exposes frontend user authentication data. Available only in Frontend context.

 

[frontend.user.isLoggedIn]
[frontend.user.userId == 5]
[4 in frontend.user.userGroupIds]

 

typo3 - TYPO3 Environment Information

Provides TYPO3 system-level metadata.

 

[typo3.version == "14.3.5"]
[typo3.branch == "14.3"]
[typo3.devIpMask == "192.168.0.100"]

 

workspace - TYPO3 Workspace Context

Exposes workspace-related information. Primarily used in Backend context.

 

[workspace.workspaceId == 1]
[workspace.isLive]
[workspace.isOffline]

 

Important Notes

  • Variables are read-only
  • Availability depends on execution context (FE / BE)
  • Prefer variables for simple, deterministic checks

Frontend-only conditions are evaluated during Frontend rendering and depend on an active HTTP request with a resolved site and language. TYPO3 v14 removed the TypoScript Frontend Controller (TSFE), so frontend conditions no longer rely on it.

If evaluated in Backend, CLI, or Scheduler, these conditions do not match, and ip() can throw an exception when no request exists.

FE Context & Evaluation Lifecycle

Frontend conditions are available:

  • After site resolution
  • After language resolution
  • During page rendering

They rely on:

  • Active request object
  • Resolved site and language
  • Resolved page arguments from routing

Frontend User & Authentication Conditions

Used to evaluate frontend user state and permissions.

 

[frontend.user.isLoggedIn]
[frontend.user.userId == 5]
[11 in frontend.user.userGroupIds]
[frontend.user.userId in [3,7,9]]
[!frontend.user.isLoggedIn]

 

Notes

Page Arguments (Replacement for getTSFE())

getTSFE() was removed in TYPO3 v14. Read the page ID and page type from the routing result instead.

 

[request?.getPageArguments()?.getPageId() == 1]
[request?.getPageArguments()?.getPageId() in [17,24]]
[backend.user.isLoggedIn]

 

Notes

  • The null-safe operator ?. works from TYPO3 v12.1
  • Existing getTSFE() conditions still run in v13.4 but must be migrated before v14

Frontend Request-Based Conditions

Used to evaluate request data, headers, and routing arguments.

 

[request && traverse(request.getQueryParams(), 'foo') == 1]
[request && traverse(request.getParsedBody(), 'foo') == 1]
[request && request.getHeaders()['host'][0] == 'www.example.org']
[request && like(traverse(request.getHeaders(), 'user-agent/0'), '*Mobile*')]
[request && request.getCookieParams()['foo'] == 1]
[request && request.getPageArguments().get('foo_id') > 0]
[request && request.getNormalizedParams().isHttps()]
[request && request.getNormalizedParams().getHttpHost() == "t3planet.com"]

 

Notes

  • Request data is unavailable in CLI
  • request always evaluates to false in page TSconfig and user TSconfig
  • Header names are lowercase, for example user-agent

Frontend Site & Language Conditions

Available only after site configuration is resolved.

 

[site("identifier") == "t3terminal"]
[site("base").getHost() == "t3planet.com"]
[site("rootPageId") == 1]
[siteLanguage("languageId") == 0]
[siteLanguage("locale").getName() == "de-CH"]
[locale().getName() == "de-DE"]
[siteLanguage("title") == "German"]

 

Notes

  • site() and siteLanguage() may return null
  • Safe only in FE context
  • locale() is available from TYPO3 v14 (changelog)

Frontend Session Conditions

Used to read values from the TYPO3 FE session.

 

[session("foo|bar") == 1234567]

 

Notes

  • Requires an active FE session
  • Not available in BE or CLI

Sovereign TYPO3 AI

AI Foundation icon

AI Foundation for TYPO3T3AF

The base every extension runs on. Manage providers, prompts, permissions and costs in one place.

Install free

Backend-only conditions are evaluated inside TYPO3 Backend context, where TSFE does not exist.

They depend on:

  • Authenticated backend user
  • Backend request context
  • Workspace configuration

Backend User & Permission Conditions

Used to control backend-specific TypoScript logic.

 

[backend.user.isLoggedIn]
[backend.user.isAdmin]
[backend.user.userId == 5]
[2 in backend.user.userGroupIds]

 

Notes

  • backend.user also works in frontend TypoScript, for example for output that only logged-in editors see
  • Permission checks are BE-only

Backend Workspace Conditions

Used to differentiate live and non-live workspaces.

 

[workspace.isLive]
[workspace.workspaceId == 1]

 

Notes

  • workspace.workspaceId == 0 means the live workspace
  • Use workspace checks for preview-only configuration

These functions work in frontend TypoScript and backend TSconfig, with one exception: ip() is frontend-only since TYPO3 v13. compatVersion("13.4") is true on 13.4 and every newer version.

 

[date("d.m.") == "24.12."]
[date("j") == 7]

[like("foobarbaz", "*bar*")]
[like("fooBarBaz", "/f[o]{2,2}[aBrz]+/")]

[request && traverse(request.getQueryParams(), 'tx_news_pi1/news') > 0]

[ip("172.18.*")]
[ip("devIP")]

[compatVersion("13.4")]
[feature("security.frontend.enforceContentSecurityPolicy") === true]
[getenv("VIRTUAL_HOST") == "t3planet.de"]

A condition that never matches is usually evaluated in the wrong context, uses a function your TYPO3 version removed, or is served from cache. Most TypoScript condition issues fail silently. The following problems account for the majority of real-world bugs.

1. getTSFE() Always Returns false

What happens

Conditions using getTSFE() never match.

Why it happens

  • TYPO3 v14 removed getTSFE(), so such conditions never evaluate to true
  • In v13.4 and older: the condition is evaluated in Backend, CLI, or Scheduler context where TSFE is not initialized

How to avoid

  • Replace it with request?.getPageArguments()?.getPageId() or the page variable
  • Never rely on it in Backend logic

2. Frontend vs Backend Context Confusion

What happens

Conditions always evaluate to false.

Why it happens

  • frontend.* used in Backend
  • backend.* used in Frontend

How to avoid

  • Keep FE and BE conditions in separate blocks
  • Never mix contexts in the same condition

3. site() or  siteLanguage() Returns null

What happens

Language or site-based conditions do not match.

Why it happens

  • Site configuration not resolved
  • Condition evaluated too early or in CLI

How to avoid

  • Use site-related conditions only in FE
  • Always assume null is possible

4. Type Mismatch (String vs Integer)

What happens

Condition looks correct but never matches.

Why it happens

  • Comparing "1" with 1
  • TypoScript constants evaluated as strings

How to avoid

  • Match data types explicitly
  • Be careful with quoted constants

5. Request Data Not Available

What happens

Request-based conditions fail unexpectedly.

Why it happens

  • Condition evaluated in CLI or BE
  • No HTTP request exists

How to avoid

  • Use request.* only in FE
  • Guard with request && and avoid during cache warmup or builds

6. A Nested Condition Closes the Outer One

What happens

The inner block applies even when the outer condition is false.

Why it happens

  • Opening a new condition automatically closes the previous one
  • Conditions cannot be placed inside curly brace blocks

How to avoid

  • Since TYPO3 v12, nest across files: put @import inside the outer condition and the inner condition in the imported file
  • Or combine both checks with && in one condition

7. Changes in Included Files Do Not Show Up

What happens

You edit a condition in an imported .typoscript file and the frontend still shows the old result.

Why it happens

  • Since TYPO3 v12, TypoScript from included files is cached and a file change does not trigger re-rendering

How to avoid

  • While logged in to the backend, reload the frontend with shift-reload
  • Use "Flush frontend caches" before you test as a normal visitor

8. Conditions Fail in Page TSconfig

What happens

A condition that works in TypoScript setup never matches in page TSconfig or user TSconfig, or ip() throws an exception.

Why it happens

  • request always evaluates to false in page and user TSconfig
  • ip() needs a request and is frontend-only since TYPO3 v13

How to avoid

  • Use backend.user, tree or site settings in TSconfig conditions

A linter catches unclosed brackets and malformed conditions before they reach production. See our tutorial on TypoScript Lint in TYPO3. The full rules are in the TypoScript syntax reference for conditions.

TypoScript conditions are powerful, but they are configuration tools, not a place for business logic. Using them correctly improves performance, upgrade safety, and long-term maintainability.

Best Practices for TypoScript Conditions

Use TypoScript conditions when you need to control configuration, not application behavior.

Recommended usage

  • Enable or disable features
  • Switch templates or assets
  • Apply environment-specific configuration
  • Control rendering based on page, site, or language

Guidelines

  • Prefer deterministic checks (page, site, applicationContext)
  • Keep expressions short and readable
  • Separate Frontend and Backend conditions clearly
  • Revalidate all conditions during TYPO3 upgrades (13.4 LTS, 14.3 LTS)

Anti-Patterns: When NOT to Use TypoScript Conditions

Avoid TypoScript conditions when logic becomes dynamic, complex, or data-dependent.

Do not use conditions for

  • Business rules or workflows
  • Complex decision trees
  • User-specific personalization logic
  • Performance-critical runtime decisions
  • External API or service calls

Better alternatives

RequirementRecommended Solution
Business logicPHP services
PersonalizationControllers / ViewHelpers
Runtime decisionsMiddleware
Feature togglesConfiguration or feature flags
Heavy logicCached configuration

Rule of thumb

If a condition:

  • Is hard to read
  • Needs comments to explain
  • Depends on runtime data

It probably does not belong in TypoScript.

One of the biggest advantages of modern TypoScript conditions is the ability to define custom conditions. Unlike legacy userFunc–based conditions, Symfony Expression Language allows clean, reusable, and upgrade-safe extensions.

Legacy vs Modern Approach (Quick Context)

TYPO3 ≤ 9.3 (Legacy)

 

[userFunc = \Vendor\Extension\UserFunc\MyUserFunc('foo')]

 

TYPO3 ≥ 9.4 (Modern)

 

[variableA === 'valueB']

 

Modern conditions are:

  • Declarative
  • Readable
  • Fully integrated into TYPO3 Core

Step 1: Register the Expression Language Provider

Register your custom provider inside the extension configuration.

File: Configuration/ExpressionLanguage.php

 

<?php
return [
'typoscript' => [
\Vendor\ExtensionName\ExpressionLanguage\CustomTypoScriptConditionProvider::class,
]
];

 

This makes your variables or functions available to TypoScript conditions.

Step 2: Implement the Custom Condition Provider

Create a provider that exposes variables to the expression language.

File: Classes/ExpressionLanguage/CustomTypoScriptConditionProvider.php

 

<?php

namespace Vendor\ExtensionName\ExpressionLanguage;

use TYPO3\CMS\Core\ExpressionLanguage\AbstractProvider;

class CustomTypoScriptConditionProvider extends AbstractProvider
{
public function __construct()
{
$this->expressionLanguageVariables = [
'variableA' => 'valueB',
];
}
}

 

Key points

  • Variables are read-only
  • Values should be deterministic
  • Avoid runtime-heavy logic here

Step 3: Use the Custom Condition in TypoScript

Once registered, the variable can be used like any built-in condition.

 

[variableA === 'valueB']
page >
page = PAGE
page.10 = TEXT
page.10.value = Matched
[GLOBAL]

 

The condition is evaluated using Symfony Expression Language and behaves consistently across TYPO3 versions.

Best Practices for Custom Conditions

  • Use custom conditions for configuration decisions only
  • Keep logic simple and predictable
  • Avoid database queries, API calls, or user-specific data
  • Always re-test custom providers during TYPO3 upgrades

Why This Approach Is Preferred

  • No deprecated APIs
  • Same provider API in TYPO3 v13.4 and v14
  • Cleaner than userFunc
  • Easier to maintain in enterprise projects

TYPO3 also allows you to register custom expression functions, not just variables. This is an advanced technique and should be used only when simple variables are not sufficient.

This approach is suitable for:

  • Reusable condition logic
  • Read-only lookups
  • Controlled external checks (with caution)

When to Use a Custom Expression Function

Use a custom function when:

  • A condition needs parameters
  • Logic cannot be expressed as a static variable
  • The result can be treated as configuration data

Do not use this for:

  • Business logic
  • User-specific decisions
  • Performance-critical rendering paths

Step 1: Implement an Expression Function Provider

Create a provider that registers a custom function for TypoScript conditions.

 

<?php

namespace Vendor\ExtensionName\TypoScript;

use Symfony\Component\ExpressionLanguage\ExpressionFunction;
use Symfony\Component\ExpressionLanguage\ExpressionFunctionProviderInterface;
use TYPO3\CMS\Core\Utility\GeneralUtility;

class CustomConditionFunctionsProvider implements ExpressionFunctionProviderInterface
{
public function getFunctions(): array
{
return [
$this->getWebserviceFunction(),
];
}

protected function getWebserviceFunction(): ExpressionFunction
{
return new ExpressionFunction(
'webservice',
function () {
// Compiler not required for TypoScript usage
},
function ($existingVariables, string $endpoint, int $uid) {
return GeneralUtility::getUrl(
'https://t3planet.de/en/endpoint/' . $endpoint . '/' . $uid
);
}
);
}
}

 

Important notes

  • Only the evaluator is used for TypoScript
  • Return values must be predictable
  • Avoid slow or unstable endpoints

Step 2: Use the Custom Function in TypoScript

Once registered, the function behaves like a native condition function.

 

[webservice('pages', 10)]
page.10 >
page.10 = TEXT
page.10.value = Matched
[GLOBAL]

 

You can also compare the returned value explicitly.

 

[webservice('pages', 10) === 'Expected title']
page.10 >
page.10 = TEXT
page.10.value = Matched
[GLOBAL]

 

Key Warnings (Read This Before Using)

  • Conditions are evaluated frequently
  • External requests can slow down rendering
  • Failures may silently break conditions

Best practice

  • Cache results aggressively
  • Keep functions side-effect free
  • Prefer static variables whenever possible

Why This Is an Advanced Pattern

  • Powerful but easy to misuse
  • Requires strong control over performance
  • Best suited for enterprise projects with strict guidelines

If a condition becomes hard to reason about, it likely belongs in:

  • PHP services
  • Controllers
  • Middleware

Modern TypoScript conditions based on Symfony Expression Language are the recommended and future-safe approach for configuring TYPO3 projects. They offer cleaner syntax, better readability, and a consistent evaluation model across Frontend and Backend.

If you’re maintaining existing TYPO3 sites, migrating legacy conditions to modern syntax should be part of every upgrade strategy. Teams modernising their wider development workflow can also use the AI Foundation for TYPO3 as a controlled foundation for AI tools and automation.

This reduces technical debt, prevents silent failures, and keeps your configuration aligned with current TYPO3 standards. The same conditions work in the TypoScript of our TYPO3 templates, for example to switch a layout per language or per page tree.

Use TypoScript conditions where they make sense, for configuration, not business logic, and always validate them during major TYPO3 upgrades. With the right structure and discipline, modern conditions become a powerful and reliable tool in everyday TYPO3 development.

More free TYPO3 guides

Upgrade, accessibility, relaunch, multilingual, MCP, EU AI Act and Cyber Resilience Act: each guide is a free PDF with an Excel worksheet you fill in.

See all free TYPO3 guides

Modern TypoScript conditions use Symfony Expression Language (introduced in TYPO3 v9.4) to evaluate logic in a single, readable expression. They replace legacy condition syntax and are upgrade-safe.

Legacy conditions like [globalVar] and [userFunc] are deprecated. They may still work in older TYPO3 versions, but should not be used in new projects or upgrades.

Yes, but not all conditions work everywhere. Some are Frontend-only (e.g. getTSFE(), siteLanguage()), while others are Backend-only (e.g. backend.user.*, workspace.*).

getTSFE() is available only during Frontend rendering. If the condition runs in Backend, CLI, or Scheduler context, it will always evaluate to false.

Create custom conditions only for configuration-level decisions that cannot be expressed with built-in variables or functions. Avoid using them for business logic or performance-critical checks.

Yes. TypoScript conditions are evaluated frequently, which is why they should remain simple, deterministic, and fast. Heavy logic or external calls can negatively impact performance.

Sovereign TYPO3 AI: AI Foundation is free and open source, six AI extensions build on it.

Explore TYPO3 AI