netzbewegung / nb-headless-content-blocks
Connects together EXT:headless and EXT:content_block
Package info
github.com/Netzbewegung-Backend/nb_headless_content_blocks
Language:Shell
Type:typo3-cms-extension
pkg:composer/netzbewegung/nb-headless-content-blocks
Requires
- friendsoftypo3/content-blocks: ^1.2.3 || ^2.0
- friendsoftypo3/headless: ^4.5 || ^5.0 || dev-master
- typo3/cms-core: ^13.4 || ^14.3
Requires (Dev)
- b13/container: ^3.1 || ^4.0
- bnf/phpstan-psr-container: ^1.1
- friendsofphp/php-cs-fixer: ^3.22
- phpstan/phpstan: ^2.1
- phpstan/phpstan-phpunit: ^2.0
- typo3/coding-standards: 0.8.x-dev
- typo3/testing-framework: ^9.5
This package is auto-updated.
Last update: 2026-07-23 21:06:11 UTC
README
TYPO3 Extension EXT:nb_headless_content_blocks
Connects together EXT:headless (friendsoftypo3/headless) and EXT:content_blocks (friendsoftypo3/content-blocks)
TYPO3 Installation
Install extension using composer
composer require netzbewegung/nb_headless_content_blocks
and then, include Site Set "Headless Content Blocks", and you are ready to go.
Features
- Converts all complex objects into an array without extra configuration
- Richtext fields are automaticly converted via
parseFunc($value, null, '< lib.parseFunc_RTE') - Additional thumbnails can be created via headless.php per Content Block
- Support for EXT:container
Custom Configuration per Content Block Type
Create headless.php inside each Content Block.
your_extension/ContentBlocks/ContentElements/your-content-block-element/headless.php
<?php
use TYPO3\CMS\Fluid\ViewHelpers\Uri\ImageViewHelper;
$generateThumbnail = function (array $arguments): string {
if (array_key_exists('absolute', $arguments) === false) {
$arguments['absolute'] = true;
}
$imageViewHelper = new ImageViewHelper();
foreach ($imageViewHelper->prepareArguments() as $argumentKey => $argumentDefinition) {
if (array_key_exists($argumentKey, $arguments) === false) {
$arguments[$argumentKey] = $argumentDefinition->getDefaultValue();
}
}
$imageViewHelper->setArguments($arguments);
return $imageViewHelper->initializeArgumentsAndRender();
};
foreach ($data['items'] ?? [] as $itemKey => $item) {
if ($item['image']) {
$image = $item['image'];
$data['items'][$itemKey]['image']['thumbnails'] = [
'mobile' => $generateThumbnail(['src' => $image['id'], 'treatIdAsReference' => true, 'width' => 320]),
'desktop' => $generateThumbnail(['src' => $image['id'], 'treatIdAsReference' => true, 'width' => 800]),
];
}
}
return $data;
Additional Data via Sub DataProcessing
tt_content.vendor_yourcontentblockelement.fields.data.dataProcessing.10 {
dataProcessing {
10 = menu
10 {
levels = 2
as = navigation
}
}
}
Custom Configuration per FieldType/FieldName/TCA-Configuration in ArrayRecursiveToArray via PSR-14 Event
Create a PSR-14 Event Listener in your extension.
Use $event->setProcessedValue($yourModifiedValueForThisField) to handle the value for specific fields.
Any unhandled fields will fall back to the default processing.
<?php
declare(strict_types=1);
namespace MyVendor\MyExtension\EventListener;
use Netzbewegung\NbHeadlessContentBlocks\Event\ModifyArrayRecursiveToArrayEvent;
use TYPO3\CMS\Core\Attribute\AsEventListener;
#[AsEventListener()]
final class MyCustomListener {
public function __invoke(ModifyArrayRecursiveToArrayEvent $event): void {
// Example: Custom handling by field name (tt_content.tx_my_vendor_field_name)
if ($event->getKey() === 'tx_my_vendor_field_name') {
$value = $event->getValue();
// Do your custom processing here
$processedValue = strtoupper($value);
// $processedValue The value which is returned for this field in json response
$event->setProcessedValue($processedValue);
}
// Example: Custom handling for all text fields
if ($event->getTcaFieldDefinition()->fieldType instanceof \TYPO3\CMS\ContentBlocks\FieldType\TextFieldType) {
$value = $event->getValue();
// Do your custom processing here
$processedValue = strtoupper($value);
// $processedValue The value which is returned for this field in json response
$event->setProcessedValue($processedValue);
}
// Example: Custom handling for a specific field which have custom field type
if ($event->getTcaFieldDefinition()->fieldType instanceof \MyVendor\MyExtension\MyCustomFieldType) {
$value = $event->getValue();
// Do your custom processing here
$processedValue = strtoupper($value);
// $processedValue The value which is returned for this field in json response
$event->setProcessedValue($processedValue);
}
}
}
Custom Configuration for EXT:container (b13/container)
TypoScript Setup
left/right parallel to data
lib.content.select.where = colPos NOT IN (201, 202)
tt_content.b13_2_columns_container =< lib.contentElement
tt_content.b13_2_columns_container {
fields {
left = TEXT
left {
dataProcessing {
10 = nb-container-json
10 {
colPos = 201
as = left
}
}
}
right = TEXT
right {
dataProcessing {
10 = nb-container-json
10 {
colPos = 202
as = right
}
}
}
}
}
left/right inside data (via Sub DataProcessing)
lib.content.select.where = colPos NOT IN (201, 202)
tt_content.b13_2_columns_container.fields.data.dataProcessing.10 {
dataProcessing {
10 = nb-container-json
10 {
colPos = 201
as = left
}
20 = nb-container-json
20 {
colPos = 202
as = right
}
}
}
Development
Setup
ddev start ddev composer install touch .Build/public/FIRST_INSTALL ddev launch chmod +x ./Build/Scripts/runTests.sh
Dependencies are installed into .Build/vendor (TYPO3 web root: .Build/public).
Testing
Unit and functional tests are based on the TYPO3 Testing Framework.
# Unit tests Build/Scripts/runTests.sh -s unit # Functional tests (sqlite by default, -d mariadb|mysql|postgres possible) Build/Scripts/runTests.sh -s functional # PHPStan Build/Scripts/runTests.sh -s phpstan # Coding guidelines (php-cs-fixer) Build/Scripts/runTests.sh -s cgl # Specific PHP version Build/Scripts/runTests.sh -s unit -p 8.4
Test structure
Tests/
├── Fixtures/Extensions/test_nb_headless_content_blocks/ # Content Block fixtures
├── Unit/ # Unit tests (no TYPO3 context)
└── Functional/ # Functional tests (isolated TYPO3 instance)
IDE: temp directories from indexing
Be sure to exclude the temp directories from indexing in your IDE before starting
the tests. Functional tests create isolated TYPO3 instances in typo3temp/var/tests,
the DDEV web container uses .Build/public/typo3temp:
.Build/public/typo3temp
PhpStorm: Right-click the directories → Mark Directory as → Excluded (or Settings → Directories → add both to Excluded files).
VS Code (.vscode/settings.json):
{
"files.exclude": {
"**/.Build/public/typo3temp": true
},
"search.exclude": {
"**/.Build/public/typo3temp": true
},
"files.watcherExclude": {
"**/.Build/public/typo3temp/**": true
}
}