Multi-channel content visibility for TYPO3. Define contexts based on various
conditions (IP address, domain, query parameters, HTTP headers, sessions, etc.)
and control content visibility across your entire TYPO3 installation.
New in version 5.0.0
TYPO3 13.4/14.3 LTS support, context aware page caching and
config.linkVars on PSR-14 events.
The Contexts extension provides a flexible system for defining contextual
conditions that control content visibility throughout your TYPO3 installation.
Instead of duplicating pages for different scenarios (mobile vs. desktop,
different countries, logged-in vs. anonymous users), you can define contexts
that automatically show or hide content based on various conditions.
Key Features
Multi-channel content management
Define contexts based on IP addresses, domains, query parameters, HTTP headers,
session data, and more. Content visibility is automatically controlled based
on the active context.
Page and content element visibility
Apply contexts to pages, content elements, and records. Elements are
automatically shown or hidden based on context matching.
Extensible architecture
Create custom context types through a clean API. The extension provides
built-in types and allows easy extension for project-specific needs.
Performance optimized
Context matching is cached and optimized for production environments.
Minimal overhead on page rendering.
Use Cases
Geographic content: Show different content based on visitor location (IP-based)
Device-specific content: Different layouts for mobile/desktop (via domain or parameter)
A/B testing: Show different content variants based on session or cookie
Environment-specific: Different behavior in staging vs. production
User-based: Content visibility based on frontend user properties
Requirements
TYPO3 v13.4 LTS or v14.3 LTS
PHP 8.2 or higher
Tip
For TYPO3 v12.4 support, use version 4.x of this extension; for TYPO3 v11,
use version 3.x.
Related Extensions
Additional context types are available through companion extensions by Netresearch:
Enable debug output in the frontend. When enabled, an HTML comment
<!-- Contexts Extension Debug Mode Active --> is added to the
page header. Use in development only.
Note
Version 5.0.0 removed the contexts.matchMode and
contexts.cacheLifetimeModifier settings. Neither of them was ever
read by the extension. Remove them from your site configuration; they
have no replacement.
Page and Content Element Settings
Context visibility is configured directly on page and content element
records via the Contexts tab in the TYPO3 backend.
Each context record appears with two options:
Visible: yes — record is only shown when the context is active
Visible: no — record is hidden when the context is active
The extension adds two database columns to controlled tables:
tx_contexts_enable
Comma-separated list of context UIDs that must be active for the
record to be visible.
tx_contexts_disable
Comma-separated list of context UIDs that hide the record when
active.
Caching Considerations
Context-dependent content affects page caching. The extension handles
this through several mechanisms:
Query restriction: The ContextRestriction class
automatically adds WHERE clauses to database queries, filtering
records based on active contexts.
Cache hash modification: When query parameter contexts are
active, the extension adds context identifiers to the page cache
hash, ensuring separate cache entries per context combination.
Menu filtering: Menu items are filtered based on context
visibility settings, so navigation reflects the current context.
Cache invalidation on context changes: TYPO3 caches the result
of
ModifyTypoScriptConfigEvent
- and with it the
config.linkVars
entries this extension adds - under an
identifier derived from the TypoScript sources, not from the context
records. Saving, deleting or restoring a context record therefore
flushes the pages cache group (hash, pages, rootline
and typoscript) from the DataHandler hook. No manual cache flush
is needed after adding a GET parameter context.
New in version 5.0.0
Before 5.0.0 a new or renamed GET parameter context did not reach
config.linkVars
until the caches were flushed by hand.
Tip
For pages that depend heavily on context state, consider using
config.no_cache = 1 in TypoScript or use context-aware
cache tags to ensure correct content delivery.
Context Types
The extension provides several built-in context types. Each type evaluates
different conditions to determine if a context is active.
Match visitors by their IP address or IP range. Supports both IPv4 and IPv6.
Configuration
IP/Range
IP/Range
type
string
required
true
Single IP address, CIDR notation, range, or comma-separated list.
Examples:
Single IP: 192.168.1.100
CIDR notation: 10.0.0.0/8, FE80::/16
Range: 192.168.1.1-192.168.1.255
Wildcards: 80.76.201.*, 80.76.*.37
Multiple: 192.168.1.0/24,10.0.0.0/8
Use Cases
Internal network detection (show admin tools for office IPs)
Geographic content (by IP geolocation with contexts_geolocation)
Office vs. external visitor differentiation
Development/staging environment detection
Domain Context
Match based on the accessed domain name (HTTP_HOST).
Configuration
Domain
Domain
type
string
required
true
Domain name(s) to match. One domain per line.
Matching Rules:
Without leading dot: Exact match only
www.example.org will not match example.org
With leading dot: Matches all subdomains
.example.org matches www.example.org, shop.example.org, etc.
Examples:
Single: www.example.com
Multiple (one per line):
example.com
www.example.com
Copied!
Subdomain wildcard: .example.com
Use Cases
Multi-domain setups (different content per domain)
Staging vs. production detection
Brand-specific content on shared installations
Language-specific domains
Query Parameter Context
Match based on URL query parameters (GET parameters).
Configuration
Parameter Name
Parameter Name
type
string
required
true
The GET parameter name to check.
Expected Value
Expected Value
type
string
required
false
Value to match. Supports regular expressions. If empty, any non-empty
value activates the context.
Store in Session
Store in Session
type
boolean
default
false
When enabled, the context state is stored in the user session, persisting
across page navigations even after the parameter is removed from the URL.
Examples:
?debug=1 with parameter debug and value 1
?variant=a for A/B testing
?affID=partner for affiliate tracking
Use Cases
A/B testing variants
Debug mode activation
Campaign and affiliate tracking
Feature flags via URL
HTTP Header Context
New in version 3.0.0
HTTP Header context for matching request headers.
Match based on HTTP request headers sent by the browser or proxy.
Configuration
Header Name
Header Name
type
string
required
true
The HTTP header name to check (case-insensitive).
Expected Value
Expected Value
type
string
required
false
Value(s) to match against the header. One value per line.
Matching uses case-insensitive substring comparison: if any
configured value appears anywhere in the actual header value,
the context matches. If empty, any non-empty header value
activates the context.
Store in Session
Store in Session
type
boolean
default
false
When enabled, the context state persists in the user session.
New in version 4.0.0
PSR-7 header support and case-insensitive substring matching.
Header Name Resolution
The extension supports both standard HTTP header names and
$_SERVER parameter keys:
The extension registers six PSR-14 event listeners that handle
context-based behavior in the frontend and backend. These listeners
are registered via PHP attributes and cannot be removed, but you
can register your own listeners on the same events.
Page Access Control
PageAccessEventListener listens to
AfterPageAndLanguageIsResolvedEvent and checks whether the
current page is accessible based on its context restrictions
(tx_contexts_enable / tx_contexts_disable). If access
is denied, it throws an ImmediateResponseException with a
403 response.
The current page is always checked for its own context
restrictions. Parent pages only propagate restrictions when
extendToSubpages is enabled.
Menu Item Filtering
MenuItemFilterEventListener listens to
FilterMenuItemsEvent and removes menu items that should
not be visible in the current context. This ensures navigation
menus reflect context-based visibility.
Icon Overlay Modification
IconOverlayEventListener listens to
ModifyRecordOverlayIconIdentifierEvent and modifies icon
overlays for records that have context-based visibility
settings, providing visual feedback in the backend.
Page Cache Identifier
New in version 5.0.0
PageCacheIdentifierEventListener listens to
BeforePageCacheIdentifierIsHashedEvent and adds the current
values of all GET parameters used by a "getparam" context to the
page cache identifier. Without it, a page rendered with a
context-switching parameter would overwrite the cache entry of the
same page rendered without it.
TypoScript config.linkVars
New in version 5.0.0
TypoScriptConfigEventListener listens to
ModifyTypoScriptConfigEvent and appends the GET parameters of
all "getparam" contexts to
config.linkVars
, so the
active channel survives following a link. It replaces the
configArrayPostProc hook that was removed in TYPO3 v13.0
(#102932).
The parameter list is derived from the configured contexts, never
from the current request, because the event result is written into
the TypoScript cache. See Caching Considerations for how that
cache is invalidated when a context record changes.
FlexForm Data Structure
New in version 5.0.0
FlexFormDataStructureEventListener listens to
BeforeFlexFormDataStructureIdentifierInitializedEvent and
BeforeFlexFormDataStructureParsedEvent and resolves the FlexForm
of
tx_contexts_contexts.type_conf
from the context type of the
record.
TYPO3 v14 removed the ds_pointerField mechanism and the
multi-entry ds array (Breaking #107047) that this used to rely on.
The replacement core suggests, record types plus
types.<type>.columnsOverrides, cannot be used here: TYPO3 v13.4
resolves the data structure from the raw TCA in DataHandler and
ReferenceIndex, where columnsOverrides is not applied. These
two events are dispatched by FlexFormTools for every caller and
have an identical API in v13.4 and v14.3.
For extension authors this is transparent:
Configuration::registerContextType()
keeps its signature, and
the FlexForm file passed as its fourth argument is still the data
structure used for that type. Only code that read the FlexForm file
back out of
$GLOBALS['TCA']['tx_contexts_contexts']['columns']['type_conf']['config']['ds']
has to switch to
Configuration::getContextTypes()[$type]['flexFile']
.
Architecture
Request Lifecycle
The ContainerInitialization PSR-15 middleware initializes
context matching on every frontend request. It:
Sets the PSR-7 request on the Container singleton
Triggers Container::initMatching() which loads all context
records from the database and runs match() on each
Only matched contexts are retained in the container
This middleware runs before the page resolver, ensuring contexts
are available throughout the rendering pipeline.
Query Restrictions
The ContextRestriction class implements
EnforceableQueryRestrictionInterface and automatically adds
WHERE clauses to all database queries on context-controlled
tables. It uses FIND_IN_SET() to check whether active
context UIDs appear in the tx_contexts_enable or
tx_contexts_disable columns.
This means records with context restrictions are automatically
filtered in all frontend queries without additional code.
Context API
Checking Contexts Programmatically
The Container class provides access to all matched contexts.
It uses a singleton pattern and extends ArrayObject.
<?phpdeclare(strict_types=1);
useNetresearch\Contexts\Context\Container;
// Get the container instance (singleton)
$container = Container::get();
// Find a specific context by alias or UID
$context = $container->find('my-context-alias');
if ($context !== null) {
// Context exists and is active (matched)
}
// Iterate over all active (matched) contextsforeach (Container::get() as $uid => $context) {
echo $context->getAlias() . ' is active';
}
Copied!
Using the ContextMatcher API
For simple matching checks, use the ContextMatcher API:
<?phpdeclare(strict_types=1);
useNetresearch\Contexts\Api\ContextMatcher;
if (ContextMatcher::getInstance()->matches('mobile')) {
// Mobile context is active
}
Copied!
Configuration API
Enable context settings on custom tables using the
Configuration API:
This registers the tx_contexts_settings column, adds flat
columns (tx_contexts_enable, tx_contexts_disable), and
integrates context visibility into the record editing form.
Record API
Check if a record is enabled for the current contexts:
<?phpdeclare(strict_types=1);
useNetresearch\Contexts\Api\Record;
// Check if a record is visible in current context
$row = ['uid' => 42, 'tx_contexts_enable' => '3,5'];
if (Record::isEnabled('tt_content', $row)) {
// Record is visible
}
// Check a specific settingif (Record::isSettingEnabled('my_table', 'my_setting', $row)) {
// Setting is enabled for this record
}
Copied!
TypoScript Conditions
Use the contextMatch() function in TypoScript conditions:
# Show content only when mobile context is active[contextMatch('mobile')]
page.10.wrap = <div class="mobile-wrapper">|</div>
[END]# Combine with other conditions[contextMatch('internal') && tree.level > 2]
lib.breadcrumb.show = 1
[END]
Copied!
The contextMatch() function is provided via TYPO3's
ExpressionLanguage and works in all condition contexts
(TypoScript, TSconfig, etc.).
Fluid ViewHelpers
The extension provides a ViewHelper for context matching in Fluid
templates.
Check if a context is active using the matches ViewHelper:
<f:ifcondition="{contexts:matches(alias: 'mobile')}"><f:then><p>Mobile context is active</p></f:then><f:else><p>Mobile context is not active</p></f:else></f:if>
Copied!
The ViewHelper returns 1 when the context matches and 0
otherwise, making it compatible with Fluid's condition evaluation.
Practical Examples
Conditional rendering based on context:
<f:ifcondition="{contexts:matches(alias: 'internal')}"><divclass="admin-toolbar"><!-- Only shown for internal network --></div></f:if>
Copied!
Combining with other conditions:
<f:ifcondition="{contexts:matches(alias: 'premium')} && {user}"><divclass="premium-content"><!-- Premium user content --></div></f:if>
Copied!
Testing
Running Tests
Tests can be run via Composer scripts or the Docker-based
runTests.sh script.
# Unit tests (default suite)
./Build/Scripts/runTests.sh -s unit
# Unit tests with coverage
./Build/Scripts/runTests.sh -s unitCoverage
# Functional tests with SQLite
./Build/Scripts/runTests.sh -s functional -d sqlite
# PHPStan
./Build/Scripts/runTests.sh -s phpstan
# With specific PHP version
./Build/Scripts/runTests.sh -s unit -p 8.4
# Show all options
./Build/Scripts/runTests.sh -h
Copied!
The Docker-based script uses ghcr.io/typo3/core-testing-php*
images and requires Docker or Podman.
Writing Tests for Custom Contexts
<?phpdeclare(strict_types=1);
namespaceVendor\MyExtension\Tests\Unit\Context\Type;
usePHPUnit\Framework\Attributes\Test;
useTYPO3\TestingFramework\Core\Unit\UnitTestCase;
useVendor\MyExtension\Context\Type\MyCustomContext;
finalclassMyCustomContextTestextendsUnitTestCase{
#[Test]publicfunctionmatchReturnsTrueForValidCondition(): void{
$context = new MyCustomContext();
// Setup test conditionsself::assertTrue($context->match());
}
}
Copied!
Debugging
Enable context debugging via the Site Set setting:
config/sites/<identifier>/config.yaml
settings:contexts:debug:true
Copied!
This adds an HTML comment to the page header indicating that
debug mode is active. For detailed context matching inspection,
use the TYPO3 Admin Panel or check the tx_contexts_contexts
table for context configuration and the tx_contexts_enable /
tx_contexts_disable columns on your records.
Migration Guide
This guide covers migrating from previous versions of the Contexts extension.
Migrating from v4.x to v5.0
Version 5.0 targets TYPO3 v13.4 LTS and v14.3 LTS and completes the removal of
the frontend APIs that TYPO3 dropped in v13 and v14.
Breaking Changes
TYPO3 Version
Before: TYPO3 v12.4 LTS and v13.4 LTS
After: TYPO3 v13.4 LTS and v14.3 LTS
TYPO3 v12.4 is no longer supported. Use version 4.x for TYPO3 v12.4
installations.
Removed API
The following methods were bound to the TypoScriptFrontendController hooks
removed in TYPO3 v13.0 (forge#102932) and are gone:
Custom context types that extend
\Netresearch\Contexts\Context\AbstractContext
are affected by the removal
of the TypoScriptFrontendController
(forge#107831) and of
GeneralUtility::getIndpEnv()
(deprecated in TYPO3 v14.3):
Removed protected method
Replacement
getTypoScriptFrontendController()
getRequest() / getFrontendUser()
getIndpEnv()
getNormalizedParams()
Fixed Behaviour
Four defects are fixed by this release:
The session context and the use_session persistence of the GET
parameter context read the frontend user from the removed
TypoScriptFrontendController. They now read the frontend.user
request attribute and match again.
Context-aware page caching andconfig.linkVars were registered on
hooks removed in TYPO3 v13.0, so every context variant of a page shared one
page cache entry and the switching GET parameter was dropped from generated
links. They now use
\TYPO3\CMS\Frontend\Event\BeforePageCacheIdentifierIsHashedEvent
and
\TYPO3\CMS\Frontend\Event\ModifyTypoScriptConfigEvent
.
The FlexForm of the context type configuration was selected via
ds_pointerField, removed in TYPO3 v14.0 (forge#107047). On v14.3
every read of tx_contexts_contexts.type_conf threw an
InvalidTcaException, so context records could neither be rendered nor
saved. See FlexForm Data Structure.
Frontend caches are invalidated when a context record changes. The
result of
ModifyTypoScriptConfigEvent
is cached under an identifier
derived from the TypoScript sources, so a new or renamed GET parameter
context previously did not reach config.linkVars until the caches were
flushed by hand. See Caching Considerations.
Removed Site Set Settings
contexts.matchMode and contexts.cacheLifetimeModifier have been removed
from Configuration/Sets/Contexts/. Neither setting was ever read by the
extension. Remove them from your site configuration; there is no replacement.
contexts.debug is unchanged.
Removed Files
ext_tables.php has been removed - it only held a TYPO3 v12 code path and the
file itself is deprecated since TYPO3 v14.3 (forge#109438). The v13+
equivalent lives in Configuration/TCA/tx_contexts_contexts.php via
ctrl.security.ignorePageTypeRestriction.
$GLOBALS['TYPO3_CONF_VARS']['FE']['addRootLineFields'] is no longer written
in ext_localconf.php; the setting was removed in TYPO3 v13.2
(forge#103752) and rootline relations on pages are always resolved since.
Step-by-Step Migration
Update TYPO3 to v13.4 or v14.3
Update extension via Composer:
composer require netresearch/contexts:^5.0
Copied!
Clear all caches:
vendor/bin/typo3 cache:flush
Copied!
Review custom context types for the removed protected methods listed
above
Migrating from v3.x to v4.0
Version 4.0 introduces significant changes for TYPO3 v12/v13 compatibility.
Breaking Changes
PHP Version
Before: PHP 7.4 - 8.1
After: PHP 8.2+
Update your deployment and CI pipelines accordingly.
TYPO3 Version
Before: TYPO3 v11
After: TYPO3 v12.4 LTS and v13.4 LTS only
TYPO3 v11 and earlier are no longer supported. Use version 3.x for older
TYPO3 installations.
Hook Migration
All SC_OPTIONS hooks have been replaced with PSR-14 events.
All notable changes to this project are documented here.
Version 5.0.0
This is a major release targeting TYPO3 v13.4 LTS and v14.3 LTS. It also fixes
several defects, two of which were already silent on TYPO3 v13.
Breaking Changes
Dropped TYPO3 v12.4 support - use version 4.x for TYPO3 v12.4
Removedext_tables.php - deprecated in TYPO3 v14.3
(forge#109438)
RemovedFrontendControllerService::registerQueryParameter(),
::createHashBase() and ::configArrayPostProc()
RemovedPageService::createHashBase() - use the public
PageService::getHashString()
Removed the empty CacheHashEventListener
Removed the protected AbstractContext::getTypoScriptFrontendController()
and AbstractContext::getIndpEnv() - custom context types use
getRequest(), getFrontendUser() and getNormalizedParams()
Dropped the unused typo3/cms-extbase and typo3/cms-extensionmanager
requirements - no class of either package is referenced
Removed the site set settings contexts.matchMode and
contexts.cacheLifetimeModifier - neither was ever read by the extension
Configuration::registerContextType()
no longer writes the FlexForm
file into TCA[tx_contexts_contexts][columns][type_conf][config][ds]; the
data structure is resolved from the context type registry instead
Bug Fixes
Context records can be edited on TYPO3 v14.3 again - ds_pointerField
and the multi-entry ds array were removed in TYPO3 v14.0
(forge#107047), so every read of tx_contexts_contexts.type_conf threw
an InvalidTcaException. FlexFormDataStructureEventListener resolves
the data structure through the PSR-14 BeforeFlexFormDataStructure* events,
which behave identically on v13.4 and v14.3
Frontend caches are invalidated when a context record changes - saving,
deleting or restoring a context flushes the pages cache group. TYPO3
caches the
ModifyTypoScriptConfigEvent
result under an identifier
derived from the TypoScript sources, so a new or renamed GET parameter
context previously never reached
config.linkVars
Session context matches again - the frontend user is read from the
frontend.user request attribute instead of the TypoScriptFrontendController
removed in TYPO3 v14 (forge#107831). The same applies to the
use_session persistence of the GET parameter context
Context-aware page caching works again - the active contexts and the
values of all context GET parameters are added to the page cache identifier
via
\TYPO3\CMS\Frontend\Event\BeforePageCacheIdentifierIsHashedEvent
,
replacing the createHashBase hook removed in TYPO3 v13.0
(forge#102932)
config.linkVars works again - the GET parameters of all query parameter
contexts are appended via
\TYPO3\CMS\Frontend\Event\ModifyTypoScriptConfigEvent
, replacing the
removed configArrayPostProc hook
Page tree restrictions are no longer dropped - the PageTreeRepository
XCLASS forwards the third $additionalQueryRestrictions constructor
argument, so options.pageTree.excludeDoktypes is honoured again
Removed the write to $TYPO3_CONF_VARS['FE']['addRootLineFields'],
removed in TYPO3 v13.2 (forge#103752)
Improvements
CI matrix covers TYPO3 ^13.4 and ^14.3 on PHP 8.2 - 8.5
composer.json declares extra.typo3/cms.version and
extra.typo3/cms.Package.providesPackages (forge#108345)
Rector and Fractor run the TYPO3 v14 rule sets; the one rule that may not be
applied while TYPO3 v13.4 is supported is skipped explicitly
PHPStan enables reportUnmatchedIgnoredErrors and drops the 27 ignore
patterns that no longer matched anything
DDEV development environment provides TYPO3 v13 and v14 instances
Version 4.0.0
Release date: 2026-03-01
This is a major release with TYPO3 v12/v13 support and significant modernization.
Breaking Changes
Dropped TYPO3 v11 support - Use version 3.x for TYPO3 v11
Dropped PHP 7.4/8.0/8.1 support - Minimum PHP 8.2 required
Removed SC_OPTIONS hooks - Migrated to PSR-14 events
PHPUnit 10/11/12/13 required - Test code must use new patterns
New Features
TYPO3 v12.4 LTS support - Full compatibility with TYPO3 v12
TYPO3 v13.4 LTS support - Full compatibility with TYPO3 v13
Site Sets support - Modern site configuration for TYPO3 v13
PHP 8.2/8.3/8.4/8.5 support - Latest PHP versions supported