---
name: typo3-content-blocks
description: Expert guidance on creating Content Elements, Record Types, Page Types, and File Types using TYPO3 Content Blocks extension - the single source of truth for content modeling.
version: 1.4.0
typo3_compatibility: "13.0 - 14.x"
related_skills:
- typo3-content-blocks-migration
triggers:
- content-blocks
- content-element
- record-type
- page-type
- file-type
- make:content-block
- friendsoftypo3/content-blocks
- irre
- collection
---
# TYPO3 Content Blocks Development
> **Compatibility:** TYPO3 v13.x and v14.x (v14 preferred)
> All code examples in this skill are designed to work on both TYPO3 v13 and v14.
## 1. The Single Source of Truth Principle
Content Blocks is the **modern approach** to creating custom content types in TYPO3. It eliminates redundancy by providing a **single YAML configuration** that generates:
- TCA (Table Configuration Array)
- Database schema (SQL)
- TypoScript rendering
- Backend forms and previews
- Labels and translations
### Why Content Blocks?
| Traditional Approach | Content Blocks Approach |
|---------------------|------------------------|
| Multiple TCA files | One `config.yaml` |
| Manual SQL definitions | Auto-generated schema |
| Separate TypoScript | Auto-registered rendering |
| Scattered translations | Single `labels.xlf` |
| Complex setup | Simple folder structure |
## 2. Installation
```bash
# Install via Composer (DDEV recommended)
ddev composer require friendsoftypo3/content-blocks
# After installation, clear caches
ddev typo3 cache:flush
```
### Security Configuration (Classic Mode)
For non-composer installations, deny web access to ContentBlocks folder:
```apache
# .htaccess addition
RewriteRule (?:typo3conf/ext|typo3/sysext|typo3/ext)/[^/]+/(?:Configuration|ContentBlocks|Resources/Private|Tests?|Documentation|docs?)/ - [F]
```
## 3. Content Types Overview
Content Blocks supports four content types:
| Type | Folder | Table | Use Case |
|------|--------|-------|----------|
| `ContentElements` | `ContentBlocks/ContentElements/` | `tt_content` | Frontend content (hero, accordion, CTA) |
| `RecordTypes` | `ContentBlocks/RecordTypes/` | Custom/existing | Structured records (news, products, team) |
| `PageTypes` | `ContentBlocks/PageTypes/` | `pages` | Custom page types (blog, landing page) |
| `FileTypes` | `ContentBlocks/FileTypes/` | `sys_file_metadata` | Extended file metadata (photographer, copyright) |
## 4. Folder Structure
```
EXT:my_sitepackage/
└── ContentBlocks/
├── ContentElements/
│ └── my-hero/
│ ├── assets/
│ │ └── icon.svg
│ ├── language/
│ │ └── labels.xlf
│ ├── templates/
│ │ ├── backend-preview.html
│ │ ├── frontend.html
│ │ └── partials/
│ └── config.yaml
├── RecordTypes/
│ └── my-record/
│ ├── assets/
│ │ └── icon.svg
│ ├── language/
│ │ └── labels.xlf
│ └── config.yaml
├── PageTypes/
│ └── blog-article/
│ ├── assets/
│ │ ├── icon.svg
│ │ ├── icon-hide-in-menu.svg
│ │ └── icon-root.svg
│ ├── language/
│ │ └── labels.xlf
│ ├── templates/
│ │ └── backend-preview.html
│ └── config.yaml
└── FileTypes/
└── image-extended/
├── language/
│ └── labels.xlf
└── config.yaml
```
## 5. Creating Content Elements
### Kickstart Command (Recommended)
```bash
# Interactive mode
ddev typo3 make:content-block
# One-liner
ddev typo3 make:content-block \
--content-type="content-element" \
--vendor="myvendor" \
--name="hero-banner" \
--title="Hero Banner" \
--extension="my_sitepackage"
# After creation, update database
ddev typo3 cache:flush -g system
ddev typo3 extension:setup --extension=my_sitepackage
```
### Minimal Content Element
```yaml
# EXT:my_sitepackage/ContentBlocks/ContentElements/hero-banner/config.yaml
name: myvendor/hero-banner
fields:
- identifier: header
useExistingField: true
- identifier: bodytext
useExistingField: true
```
### Full Content Element Example
```yaml
# EXT:my_sitepackage/ContentBlocks/ContentElements/hero-banner/config.yaml
name: myvendor/hero-banner
group: default
description: "A full-width hero banner with image and CTA"
prefixFields: true
prefixType: full
basics:
- TYPO3/Appearance
- TYPO3/Links
fields:
- identifier: header
useExistingField: true
- identifier: subheadline
type: Text
label: Subheadline
- identifier: hero_image
type: File
minitems: 1
maxitems: 1
allowed: common-image-types
- identifier: cta_link
type: Link
label: Call to Action Link
- identifier: cta_text
type: Text
label: Button Text
```
### Frontend Template
```html
{data.header}
{data.subheadline}
{data.cta_text -> f:or(default: 'Learn more')}
```
### Backend Preview Template
```html
{data.header}
{data.subheadline}
```
## 6. Creating Record Types (Custom Tables)
Record Types create **custom database tables** for structured data like teams, products, events, etc.
### Extbase-Compatible Table Naming
**IMPORTANT:** For Extbase compatibility, use the `tx_extensionkey_domain_model_*` naming convention:
```yaml
# ✅ CORRECT - Extbase compatible table name
name: myvendor/team-member
table: tx_mysitepackage_domain_model_teammember
labelField: name
fields:
- identifier: name
type: Text
- identifier: position
type: Text
- identifier: email
type: Email
- identifier: photo
type: File
allowed: common-image-types
maxitems: 1
```
```yaml
# ❌ WRONG - Short table names don't work with Extbase
name: myvendor/team-member
table: team_member # Won't work with Extbase!
```
### Minimal Record Type
```yaml
# EXT:my_sitepackage/ContentBlocks/RecordTypes/team-member/config.yaml
name: myvendor/team-member
table: tx_mysitepackage_domain_model_teammember
labelField: name
fields:
- identifier: name
type: Text
```
### Full Record Type Example
```yaml
# EXT:my_sitepackage/ContentBlocks/RecordTypes/team-member/config.yaml
name: myvendor/team-member
table: tx_mysitepackage_domain_model_teammember
labelField: name
fallbackLabelFields:
- email
languageAware: true
workspaceAware: true
sortable: true
softDelete: true
trackCreationDate: true
trackUpdateDate: true
internalDescription: true
restriction:
disabled: true
startTime: true
endTime: true
security:
ignorePageTypeRestriction: true # Allow on normal pages
fields:
- identifier: name
type: Text
required: true
- identifier: position
type: Text
- identifier: email
type: Email
- identifier: phone
type: Text
- identifier: bio
type: Textarea
enableRichtext: true
- identifier: photo
type: File
allowed: common-image-types
maxitems: 1
- identifier: social_links
type: Collection
labelField: platform
fields:
- identifier: platform
type: Select
items:
- label: LinkedIn
value: linkedin
- label: Twitter/X
value: twitter
- label: GitHub
value: github
- identifier: url
type: Link
```
### Multi-Type Records (Single Table Inheritance)
Create multiple types for one table:
```yaml
# EXT:my_sitepackage/ContentBlocks/RecordTypes/person-employee/config.yaml
name: myvendor/person-employee
table: tx_mysitepackage_domain_model_person
typeField: person_type
typeName: employee
priority: 999 # Default type (loaded first)
labelField: name
languageAware: false
workspaceAware: false
fields:
- identifier: name
type: Text
- identifier: department
type: Text
```
```yaml
# EXT:my_sitepackage/ContentBlocks/RecordTypes/person-contractor/config.yaml
name: myvendor/person-contractor
table: tx_mysitepackage_domain_model_person
typeName: contractor
fields:
- identifier: name
type: Text
- identifier: company
type: Text
- identifier: contract_end
type: DateTime
```
### Record Types as Collection Children
Define a record that can be used in IRRE collections:
```yaml
# EXT:my_sitepackage/ContentBlocks/RecordTypes/slide/config.yaml
name: myvendor/slide
table: tx_mysitepackage_domain_model_slide
labelField: title
fields:
- identifier: title
type: Text
- identifier: image
type: File
maxitems: 1
- identifier: link
type: Link
```
```yaml
# EXT:my_sitepackage/ContentBlocks/ContentElements/slider/config.yaml
name: myvendor/slider
fields:
- identifier: slides
type: Collection
foreign_table: tx_mysitepackage_domain_model_slide
shareAcrossTables: true
shareAcrossFields: true
minitems: 1
```
## 7. Creating Page Types (Custom doktypes)
Page Types extend the `pages` table with custom page types – ideal for blog articles, landing pages, news pages, or other page variants with special properties.
### When to Use Page Types
| Use Case | Example |
|----------|---------|
| Structured page properties | Blog with author, teaser image, publish date |
| Plugin integration | News lists, event calendars reading page properties |
| Different page behavior | Landing pages without navigation |
| SEO-specific fields | Custom meta fields per page type |
### Minimal Page Type
```yaml
# EXT:my_sitepackage/ContentBlocks/PageTypes/blog-article/config.yaml
name: myvendor/blog-article
typeName: 1705234567
fields:
- identifier: author_name
type: Text
```
### Full Page Type Example
```yaml
# EXT:my_sitepackage/ContentBlocks/PageTypes/blog-article/config.yaml
name: myvendor/blog-article
typeName: 1705234567 # Unix timestamp (unique identifier)
group: default # Options: default, link, special
fields:
- identifier: author_name
type: Text
label: Author
required: true
- identifier: teaser_text
type: Textarea
label: Teaser
- identifier: hero_image
type: File
allowed: common-image-types
maxitems: 1
- identifier: publish_date
type: DateTime
label: Publish Date
- identifier: reading_time
type: Number
label: Reading Time (minutes)
```
### Page Type Options
| Option | Type | Required | Description |
|--------|------|----------|-------------|
| `typeName` | integer | ✓ | Unique doktype number (use Unix timestamp) |
| `group` | string | | Group in selector: `default`, `link`, `special` |
**Reserved typeName values:** 199, 254 (cannot be used)
### Icons for Page States
Page Types support state-specific icons. Add these to your assets folder:
```
ContentBlocks/PageTypes/blog-article/
├── assets/
│ ├── icon.svg # Default icon
│ ├── icon-hide-in-menu.svg # Hidden in menu state
│ └── icon-root.svg # Site root state
└── config.yaml
```
### Backend Preview
Create a `backend-preview.html` to preview custom page properties:
```html
Author: {data.author_name}
Published: {data.publish_date}
```
### Frontend Integration
Page Types have **no automatic frontend rendering**. Add the ContentBlocksDataProcessor to your TypoScript:
```typoscript
# Configuration/TypoScript/setup.typoscript
page = PAGE
page {
10 = FLUIDTEMPLATE
10 {
templateName = Default
templateRootPaths.10 = EXT:my_sitepackage/Resources/Private/Templates/
dataProcessing {
# Process Content Blocks page data
1 = content-blocks
}
}
}
```
Then access fields in your Fluid template:
```html
By {data.author_name}
```
### Remove from Page Tree Drag Area
To hide your page type from the "Create new page" drag area:
```typoscript
# Configuration/user.tsconfig
options {
pageTree {
doktypesToShowInNewPageDragArea := removeFromList(1705234567)
}
}
```
## 8. Creating File Types (Extended Metadata)
> New in version 1.2
File Types extend the `sys_file_metadata` table with custom fields – perfect for photographer credits, copyright notices, or additional file options.
### Available File Type Names
| typeName | File Types |
|----------|------------|
| `image` | JPEG, PNG, GIF, WebP, SVG |
| `video` | MP4, WebM, OGG |
| `audio` | MP3, WAV, OGG |
| `text` | TXT, PDF, Markdown |
| `application` | ZIP, Office formats |
### Minimal File Type
```yaml
# EXT:my_sitepackage/ContentBlocks/FileTypes/image-extended/config.yaml
name: myvendor/image-extended
typeName: image
fields:
- identifier: photographer
type: Text
label: Photographer
```
### Full File Type Example
```yaml
# EXT:my_sitepackage/ContentBlocks/FileTypes/image-extended/config.yaml
name: myvendor/image-extended
typeName: image
prefixFields: false # Keep original column names
fields:
- identifier: image_overlay_palette
type: Palette
label: 'LLL:EXT:core/Resources/Private/Language/locallang_tca.xlf:sys_file_reference.imageoverlayPalette'
fields:
# Reuse existing TYPO3 core fields
- identifier: alternative
useExistingField: true
- identifier: description
useExistingField: true
- type: Linebreak
- identifier: link
useExistingField: true
- identifier: title
useExistingField: true
- type: Linebreak
# Custom fields
- identifier: photographer
type: Text
label: Photographer
- identifier: copyright
type: Text
label: Copyright Notice
- identifier: source_url
type: Link
label: Source URL
- type: Linebreak
- identifier: crop
useExistingField: true
```
### File Type Options
| Option | Type | Required | Description |
|--------|------|----------|-------------|
| `typeName` | string | ✓ | One of: `text`, `image`, `audio`, `video`, `application` |
| `prefixFields` | boolean | | Disable prefixing (recommended: `false`) |
### Use Cases for File Types
| Use Case | Fields to Add |
|----------|---------------|
| Photography agency | `photographer`, `copyright`, `license_type`, `expiry_date` |
| Video platform | `director`, `duration`, `transcript`, `subtitles` |
| Document management | `document_version`, `author`, `confidentiality` |
| E-commerce | `product_sku`, `variant_color`, `variant_size` |
### Accessing File Type Fields
In Fluid templates, access custom metadata through FAL references:
```html
Photo: {image.properties.photographer}
| © {image.properties.copyright}
```
## 9. Field Types Reference
### Simple Fields
| Type | Description | Example |
|------|-------------|---------|
| `Text` | Single line text | `type: Text` |
| `Textarea` | Multi-line text | `type: Textarea` |
| `Email` | Email address | `type: Email` |
| `Link` | Link/URL | `type: Link` |
| `Number` | Integer/Float | `type: Number` |
| `DateTime` | Date and/or time | `type: DateTime` |
| `Color` | Color picker | `type: Color` |
| `Checkbox` | Boolean checkbox | `type: Checkbox` |
| `Radio` | Radio buttons | `type: Radio` |
| `Slug` | URL slug | `type: Slug` |
| `Password` | Password field | `type: Password` |
### Relational Fields
| Type | Description | Example |
|------|-------------|---------|
| `File` | File references (FAL) | `type: File` |
| `Relation` | Record relations | `type: Relation` |
| `Select` | Dropdown selection | `type: Select` |
| `Category` | System categories | `type: Category` |
| `Collection` | Inline records (IRRE) | `type: Collection` |
| `Folder` | Folder reference | `type: Folder` |
| `Language` | Language selector | `type: Language` |
### Structural Fields
| Type | Description | Example |
|------|-------------|---------|
| `Tab` | Tab separator | `type: Tab` |
| `Palette` | Group fields | `type: Palette` |
| `Linebreak` | Line break in palette | `type: Linebreak` |
| `FlexForm` | FlexForm container | `type: FlexForm` |
| `Json` | JSON field | `type: Json` |
### Common Field Options
```yaml
fields:
- identifier: my_field
type: Text
label: My Field Label # Static label (or use labels.xlf)
description: Help text # Field description
required: true # Make field required
default: "Default value" # Default value
placeholder: "Enter text..." # Placeholder text
prefixField: false # Disable prefixing for this field
useExistingField: true # Reuse existing TCA field
displayCond: 'FIELD:other:=:1' # Conditional display
onChange: reload # Reload form on change
```
### File Field Example
```yaml
fields:
- identifier: gallery_images
type: File
allowed: common-image-types
minitems: 1
maxitems: 10
appearance:
createNewRelationLinkTitle: Add Image
showAllLocalizationLink: true
behaviour:
allowLanguageSynchronization: true
```
### Select Field Example
```yaml
fields:
- identifier: layout
type: Select
renderType: selectSingle
default: default
items:
- label: Default Layout
value: default
- label: Wide Layout
value: wide
- label: Compact Layout
value: compact
```
### Collection Field Example (Inline IRRE)
```yaml
fields:
- identifier: accordion_items
type: Collection
labelField: title
minitems: 1
maxitems: 20
appearance:
collapseAll: true
levelLinksPosition: both
fields:
- identifier: title
type: Text
required: true
- identifier: content
type: Textarea
enableRichtext: true
- identifier: is_open
type: Checkbox
label: Initially Open
```
## 10. Field Prefixing
Content Blocks automatically prefixes field identifiers to avoid collisions.
### Prefixing Types
```yaml
# Full prefix (default): myvendor_myblock_fieldname
name: myvendor/my-block
prefixFields: true
prefixType: full
# Vendor prefix only: myvendor_fieldname
name: myvendor/my-block
prefixFields: true
prefixType: vendor
# Custom vendor prefix: tx_custom_fieldname
name: myvendor/my-block
prefixFields: true
prefixType: vendor
vendorPrefix: tx_custom
# No prefix (use with caution!)
name: myvendor/my-block
prefixFields: false
```
### Disable Prefixing per Field
```yaml
fields:
- identifier: my_custom_field
type: Text
prefixField: false # This field won't be prefixed
```
## 11. Templating Features
### Accessing Data in Fluid
```html
{data.header}
{data.my_field}
{data.uid}
{data.pid}
{data.languageId}
{data.mainType}
{data.recordType}
{data.fullType}
{data.rawRecord.some_field}
{data.systemProperties.createdAt}
{data.systemProperties.lastUpdatedAt}
{data.systemProperties.sorting}
{data.systemProperties.disabled}
{data.languageInfo.translationParent}
{data.languageInfo.translationSource}
{item.title}
{item.content}
```
### Asset ViewHelpers
```html
```
### Translation ViewHelper
```html
```
## 12. Extending Existing Tables
Add custom types to existing tables (like `tx_news`):
```yaml
# EXT:my_sitepackage/ContentBlocks/RecordTypes/custom-news/config.yaml
name: myvendor/custom-news
table: tx_news_domain_model_news
typeName: custom_news
fields:
- identifier: title
useExistingField: true
- identifier: custom_field
type: Text
```
## 13. Workflow with DDEV
### Standard Development Workflow
```bash
# 1. Create new Content Block
ddev typo3 make:content-block
# 2. Clear system caches
ddev typo3 cache:flush -g system
# 3. Update database schema
ddev typo3 extension:setup --extension=my_sitepackage
# Alternative: Use Database Analyzer in TYPO3 Backend
# Admin Tools > Maintenance > Analyze Database Structure
```
### Using webprofil/make Extension
If `webprofil/make` is installed:
```bash
# Create Content Block with webprofil/make
ddev make:content_blocks
# Clear caches and update database
ddev typo3 cache:flush
ddev typo3 database:updateschema
```
### Integration with Extbase
After creating Record Types with proper table names, generate Extbase models:
```bash
# If typo3:make:model is available
ddev typo3 make:model --extension=my_sitepackage
# Generate repository
ddev typo3 make:repository --extension=my_sitepackage
```
## 14. Defaults Configuration
Create a `content-blocks.yaml` in project root for default settings:
```yaml
# content-blocks.yaml
vendor: myvendor
extension: my_sitepackage
content-type: content-element
skeleton-path: content-blocks-skeleton
config:
content-element:
basics:
- TYPO3/Appearance
- TYPO3/Links
group: common
prefixFields: true
prefixType: full
record-type:
prefixFields: true
prefixType: vendor
vendorPrefix: tx_mysitepackage
```
## 15. Best Practices
### DO ✅
1. **Use Extbase-compatible table names** for Record Types:
```yaml
table: tx_myextension_domain_model_myrecord
```
2. **Reuse existing fields** when possible:
```yaml
- identifier: header
useExistingField: true
```
3. **Group related fields** with Tabs and Palettes:
```yaml
- identifier: settings_tab
type: Tab
label: Settings
```
4. **Use meaningful identifiers** (snake_case):
```yaml
- identifier: hero_background_image
```
5. **Clear caches after changes**:
```bash
ddev typo3 cache:flush -g system
ddev typo3 extension:setup --extension=my_sitepackage
```
6. **Use labels.xlf** for all user-facing labels
### DON'T ❌
1. **Don't use raw SQL** - Content Blocks generates schema automatically
2. **Don't duplicate TCA** - Config.yaml is the single source of truth
3. **Don't use short table names** for Extbase integration:
```yaml
# ❌ Wrong
table: team_member
# ✅ Correct
table: tx_mysitepackage_domain_model_teammember
```
4. **Don't use dashes in identifiers**:
```yaml
# ❌ Wrong
identifier: hero-image
# ✅ Correct
identifier: hero_image
```
5. **Don't forget shareAcross options** when using foreign_table in multiple places
## 16. Troubleshooting
### Content Block Not Appearing
```bash
# Clear all caches
ddev typo3 cache:flush
# Rebuild class loading
ddev composer dump-autoload
# Check extension setup
ddev typo3 extension:setup --extension=my_sitepackage
```
### Database Errors
```bash
# Update database schema
ddev typo3 database:updateschema
# Or use Compare Tool
# Admin Tools > Maintenance > Analyze Database Structure
```
### Field Not Saving
- Check field identifier is unique (use prefixing)
- Verify field type is correct
- Check for typos in config.yaml
- Ensure labels.xlf has matching keys
## 17. Version Constraints
```php
// ext_emconf.php
$EM_CONF[$_EXTKEY] = [
'title' => 'My Extension',
'version' => '1.0.0',
'state' => 'stable',
'constraints' => [
'depends' => [
'typo3' => '13.0.0-14.99.99',
'content_blocks' => '2.0.0-2.99.99',
],
],
];
```
---
## Related Skills
For migration between classic TYPO3 extensions and Content Blocks, see the dedicated migration skill:
- **[typo3-content-blocks-migration](./SKILL-MIGRATION.md)** - Bidirectional migration guide with:
- TCA → Content Blocks field mapping
- Content Blocks → TCA reverse mapping
- Data migration scripts
- Step-by-step examples
- Checklists for both directions
---
## 18. Package Management for v14 Compatibility
If extensions don't support TYPO3 v14 yet, fork and update:
```bash
# Add private fork as VCS repository in composer.json
# Then update extension for v14 using typo3-rector and typo3-update skills
```
Required private forks for webconsulting stack:
- `webprofil/wp-t3monitoring-client` (^13 → ^14)
- `webprofil/deployer` (^13 → ^14)
- `webprofil/crawler` (^1.0 → compatible with v14)
- `webprofil/make` (^1.1 → compatible with v14)
---
## References
- [Content Blocks Documentation](https://docs.typo3.org/p/friendsoftypo3/content-blocks/main/en-us/)
- [YAML Reference](https://docs.typo3.org/p/friendsoftypo3/content-blocks/main/en-us/YamlReference/Index.html)
- [Field Types](https://docs.typo3.org/p/friendsoftypo3/content-blocks/main/en-us/YamlReference/FieldTypes/Index.html)
- [Content Elements API](https://docs.typo3.org/p/friendsoftypo3/content-blocks/main/en-us/API/ContentElements/Index.html)
- [Record Types API](https://docs.typo3.org/p/friendsoftypo3/content-blocks/main/en-us/API/RecordTypes/Index.html)
- [Page Types API](https://docs.typo3.org/p/friendsoftypo3/content-blocks/main/en-us/API/PageTypes/Index.html)
- [File Types YAML Reference](https://docs.typo3.org/p/friendsoftypo3/content-blocks/main/en-us/YamlReference/ContentTypes/FileTypes/Index.html)
- [Migration Skill](./SKILL-MIGRATION.md)
- [Packagist: friendsoftypo3/content-blocks](https://packagist.org/packages/friendsoftypo3/content-blocks)
---
## Credits & Attribution
This skill incorporates information from the official Content Blocks documentation maintained by the **TYPO3 Content Types Team** and **Friends of TYPO3**.
Original documentation: https://docs.typo3.org/p/friendsoftypo3/content-blocks/
Adapted by webconsulting.at for this skill collection