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.
What Is a TypoScript Condition? (Short Answer)
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 withrequest?.getPageArguments()?.getPageId().loginUser()andusergroup()were deprecated in v12 and removed in v13. Usefrontend.userandbackend.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
TypoScript Conditions Cheatsheet (Quick Reference)
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 case | TypoScript condition | Context | Works in |
| Current page UID | [traverse(page, "uid") == 17] | FE | v9.4+ |
| Several pages | [traverse(page, "uid") in [17,24]] | FE | v9.4+ |
| Page UID range | [traverse(page, "uid") in 10..20] | FE | v13.4, v14 |
| Page ID from routing (replaces getTSFE().id) | [request?.getPageArguments()?.getPageId() == 42] | FE | v12.1+ |
| Page type, for example 98 | [request && request.getPageArguments()?.getPageType() == 98] | FE | v12.1+ |
| Backend layout with inheritance | [tree.pagelayout === "pagets__Home"] | FE | v13.4, v14 |
| Root level page | [tree.level == 0] | FE | v9.4+ |
| Page 2 is in the rootline | [2 in tree.rootLineIds] | FE | v9.4+ |
| Any subpage of page 30 | [30 in tree.rootLineParentIds] | FE | v10.3+ |
Site and language
| Use case | TypoScript condition | Context | Works in |
| Language ID | [siteLanguage("languageId") == 1] | FE | v9.4+ |
| Locale of the site language | [siteLanguage("locale").getName() == "de-DE"] | FE | v13.4, v14 |
| Locale shortcut | [locale().getName() == "de-DE"] | FE | v14 |
| Site identifier | [site("identifier") == "main"] | FE | v9.4+ |
| Host of the site base | [site("base").getHost() == "www.example.org"] | FE | v13.4, v14 |
| Custom site setting | [traverse(site("configuration"), "settings/some/setting") == "someValue"] | FE | v13.4, v14 |
Frontend and backend users
| Use case | TypoScript condition | Context | Works in |
| Frontend user logged in | [frontend.user.isLoggedIn] | FE | v9.4+ |
| Frontend user not logged in | [!frontend.user.isLoggedIn] | FE | v9.4+ |
| Frontend user in group 11 | [11 in frontend.user.userGroupIds] | FE | v11.2+ |
| Backend user logged in | [backend.user.isLoggedIn] | FE / BE | v9.4+ |
| Backend user is admin | [backend.user.isAdmin] | FE / BE | v9.4+ |
| Backend user in group 2 | [2 in backend.user.userGroupIds] | FE / BE | v11.2+ |
Request: host, HTTPS, parameters, device
| Use case | TypoScript condition | Context | Works in |
| HTTPS request | [request && request.getNormalizedParams().isHttps()] | FE | v9.4+ |
| Exact host | [request && request.getNormalizedParams().getHttpHost() == "example.org"] | FE | v9.4+ |
| Any subdomain | [request && like(request.getNormalizedParams().getHttpHost(), "*.example.org")] | FE | v9.4+ |
| GET parameter tx_news_pi1[news] | [request && traverse(request.getQueryParams(), 'tx_news_pi1/news') > 0] | FE | v9.4+ |
| Cookie value | [request && request.getCookieParams()['foo'] == 1] | FE | v9.4+ |
| User agent contains "Mobile" | [request && like(traverse(request.getHeaders(), 'user-agent/0'), '*Mobile*')] | FE | v13.4, v14 |
Context, version, IP and date
| Use case | TypoScript condition | Context | Works in |
| Development context | [applicationContext == "Development"] | FE / BE | v9.4+ |
| Production and its sub-contexts | [applicationContext matches "/^Production/"] | FE / BE | v9.4+ |
| TYPO3 13.4 or newer | [compatVersion("13.4")] | FE / BE | v9.4+ |
| Exact TYPO3 branch | [typo3.branch == "14.3"] | FE / BE | v9.4+ |
| Client IP matches devIPmask | [ip("devIP")] | FE | v9.4+, FE only since v13 |
| Feature toggle enabled | [feature("security.frontend.enforceContentSecurityPolicy") === true] | FE / BE | v13.4, v14 |
| Friday | [date("w") == 5] | FE / BE | v9.4+ |
| Between 9:00 and 16:59 | [date("G") >= 9 && date("G") < 17] | FE / BE | v9.4+ |
| Live workspace | [workspace.workspaceId == 0] | FE / BE | v9.4+ |
| Environment variable | [getenv("VIRTUAL_HOST") == "www.example.org"] | FE / BE | v9.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.
TYPO3 Version Compatibility for Symfony TypoScript Conditions
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 version | Expression syntax | What changed for conditions |
| 9.3 and older | No | Legacy syntax only |
| 9.4 / 9.5 LTS | Yes | Symfony Expression Language added in 9.4, legacy syntax deprecated in 9.5 |
| 10 LTS | Yes | Legacy conditions removed; tree.rootLineParentIds added in 10.3 |
| 11 LTS | Yes | userGroupIds arrays for frontend and backend users added in 11.2 |
| 12 LTS | Yes | New TypoScript parser: [END] equals [GLOBAL], @import inside conditions, null-safe ?. (12.1); loginUser() and usergroup() deprecated |
| 13.4 LTS | Yes | loginUser() and usergroup() removed, ip() frontend-only, <INCLUDE_TYPOSCRIPT: deprecated |
| 14.3 LTS | Yes | getTSFE() 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
ReplaceloginUser()andusergroup() - TYPO3 13.4 to 14
Replace everygetTSFE()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
Symfony Expression Language in TYPO3 TypoScript Conditions
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.
Old vs New TypoScript Conditions (Before & After Comparison)
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 condition | Replacement | Note |
[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.
Understanding Variables and Functions in TypoScript Conditions
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
Available TypoScript Condition Variables
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 TypoScript Conditions
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
- usergroup() and loginUser() were deprecated in TYPO3 v12 and removed in v13; use frontend.user instead
- Anonymous users always return false
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 for TYPO3T3AF
The base every extension runs on. Manage providers, prompts, permissions and costs in one place.
Backend-Only TypoScript Conditions
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
Utility Functions for Frontend & Backend
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"]
Why Does My TypoScript Condition Not Match? (Troubleshooting)
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 thepagevariable - 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 Backendbackend.*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
@importinside 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
requestalways evaluates to false in page and user TSconfigip()needs a request and is frontend-only since TYPO3 v13
How to avoid
- Use
backend.user,treeor 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.
Best Practices & Anti-Patterns for TypoScript 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
| Requirement | Recommended Solution |
| Business logic | PHP services |
| Personalization | Controllers / ViewHelpers |
| Runtime decisions | Middleware |
| Feature toggles | Configuration or feature flags |
| Heavy logic | Cached 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.
Creating Custom TypoScript Conditions with Symfony Expression Language
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
<?phpreturn ['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
<?phpnamespace 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 = PAGEpage.10 = TEXTpage.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
Advanced Custom TypoScript Conditions with Symfony Expression Language
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.
<?phpnamespace 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 = TEXTpage.10.value = Matched[GLOBAL]
You can also compare the returned value explicitly.
[webservice('pages', 10) === 'Expected title']page.10 >page.10 = TEXTpage.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
Conclusion
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.
FAQs
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
Wolfgang Weber
Brand & Communication LeadWolfgang Weber shapes TYPO3 with passion and expertise. As a TYPO3 enthusiast at T3Planet Shop, he has contributed to TYPO3 projects that make websites faster and more secure. Outside of TYPO3, you’ll probably find him exploring local cafés and enjoying football.
More From Author