Grid Elements 

Extension key

gridelements

Package name

gridelementsteam/gridelements

Version

13.1

Language

en

Author

Jo Hasenau & Contributors

License

This document is published under the Creative Commons BY 4.0 license.

Rendered

Mon, 07 Sep 2026 21:45:58 +0000


The TYPO3 extension Grid Elements carries forward the structural authoring concept of TYPO3 Backend Layouts from the page level into content-element level. Grid definitions are configured with TSconfig, which makes them reusable, maintainable and suitable for version control alongside the rest of a project's configuration.

A Grid Element defines explicit structural areas in which editors can place content. These structures can be nested and can express hierarchy, grouping, functional roles, restrictions and context. Structure therefore does more than determine where content appears. It can become part of what that content means and how it behaves.

Editors work with these structures directly in the TYPO3 backend through drag and drop and the standard New Content Element Wizard. Grid Elements also provides a sophisticated Drag-In Wizard that exposes suitable elements directly in the page layout, allowing editors to create and place them in a single drag-in interaction without first opening the New Content Element Wizard. Configurable rules can define which content is allowed in individual grid areas. Existing content can also be referenced instead of duplicated, allowing the same content to participate in different structures while remaining a single content record.


Table of Contents:

Introduction 

Grid Elements carries forward the structural authoring concept of TYPO3 Backend Layouts from page level into content structures. A Grid Element is a content element that defines an explicit structural area in which editors place other content elements, including further Grid Elements. Grid definitions themselves are configured with TSconfig, the same configuration language used for Backend Layouts, which keeps them reusable, maintainable and suitable for version control alongside the rest of a project's configuration.

What it contains and organizes 

A Grid Element's children are ordinary tt_content records related to it through a dedicated parent-child relation, not through their position on the page, see Data Model for exactly how that is stored. Because a child can be any content element type, including another Grid Element, Grid Elements can be nested structurally across multiple levels without a separate setting being required to enable nesting, see Nesting.

More than visual placement 

These structural areas can express hierarchy, grouping, functional roles, restrictions and context, not only visual arrangement. A cell can declare which content types belong there and how many, or whether a given layout may be nested inside another at all. These rules are applied by the backend authoring workflow when editors create, place and move content, see Restrictions, Validation & Permissions. Structure therefore does more than determine where content appears; it can become part of what that content means and how it behaves. Core Concepts describes this authoring model, Structure-first Authoring, in more depth.

Authoring in the backend 

The Grid Wizard is TYPO3 Core's visual tool for defining a page's Backend Layout structure. Grid Elements provides its own Grid Wizard, carrying that same structural-definition concept to content-element level and allowing a Grid Element's row and column structure to be defined visually.

Editors add content to that structure with drag and drop and TYPO3's New Content Element Wizard, or more directly with the sophisticated Drag-In Wizard. It exposes suitable elements right inside the page layout and combines their selection, creation and placement in a single drag-in interaction. Used consistently, the Drag-In Wizard covers that workflow directly without requiring the New Content Element Wizard to be opened separately, letting an editor build structure and content together in place. Existing content can also be referenced into a structure instead of duplicated, letting the same content participate in more than one structure while remaining a single record.

Where Grid Elements comes from 

Grid Elements continues a line of structural authoring work that began in 2009 with the Grid View, Grid Wizard and Backend Layout concepts. Part of that page-level approach entered TYPO3 Core with TYPO3 4.5, while Grid Elements carried the broader concept forward to content-element level. See History and Design Background for that background, and the Coders.Care article Grid Elements: The Idea for the fuller narrative.

How Grid Elements relates to other extensions 

Grid Elements is sometimes discussed as interchangeable with narrower, container-only extensions such as b13/container (EXT:container). It is not: container behavior is one part of the broader Structure-first Authoring model described above, not the whole of it. See Grid Elements and EXT:container for a direct, technical comparison.

Core Concepts 

This page introduces the vocabulary used throughout the rest of the documentation, and the authoring model that vocabulary serves. It assumes no prior reading; the technical pages under Data Model & Architecture pick up where this one ends.

Vocabulary 

Term Meaning
Grid Element A content element that defines a grid-based structure in which other content elements can be organized.
Container A Grid Element in its role as parent. The term appears throughout the technical pages for the record on the parent side of a relation.
Child A content element placed inside a container. A child can be any content element type, including another Grid Element.
Row / Column The grid structure a layout defines, the same concept as a page's Backend Layout, applied to a container instead of a page.
Cell A single position within that grid, where a row and column meet. A container's children each occupy one cell.
Nesting Placement of a Grid Element inside another Grid Element, adding another structural level. No separate setting is required to enable structural nesting, see Nesting.
Restriction Configuration on a cell that governs what may be placed there and how many elements are allowed, see Restrictions, Validation & Permissions.
Grid Wizard The visual tool for defining a grid's row and column structure. See below.
New Content Element Wizard TYPO3 Core's standard tool for selecting and creating a content element. See below.
Drag-In Wizard Grid Elements' tool for creating and placing a content element directly in its structural area, in one interaction. See below.

Structure-first Authoring 

Structure is not merely where content is placed. Structure can be part of what content means.

With Grid Elements, an editor can create a structure, a container with its rows, columns and cells, before any content values exist inside it. The structure describes a hierarchy and a grouping, which elements belong together and in what arrangement, before it describes any specific content.

That structure can also carry more than arrangement. A cell can declare which content types belong in it and how many, giving it a functional role rather than just a position, see Restrictions, Validation & Permissions. None of this depends on how the structure is rendered. The parent-child relationships and structural assignments created while editing remain available to frontend processing and templates, while restrictions govern what editors are allowed to create and place in that structure. See Data Model for how the persistent structural relationships are stored, and Parent-Child Relations and IRRE for how the parent-child relation is maintained as editors work. Structure-first Authoring is the description of that authoring model: relationships, roles and constraints defined through the structure itself, available to be filled with content, rather than inferred afterwards from where elements happen to sit.

Defining structure, then filling it 

Grid Elements provides three related but distinct tools that put Structure-first Authoring into practice. They are easy to conflate because all three appear while an editor is building a page.

The Grid Wizard belongs to the structure-definition layer. It is a visual tool for defining a grid's rows and columns and how they relate, the same concept TYPO3 Core provides for page-level Backend Layouts, carried forward by Grid Elements to content-element level. Using it does not place any content; it defines the structural area content will later go into.

The New Content Element Wizard is TYPO3 Core's standard mechanism for selecting and creating a content element. Grid Elements integrates with it like any other content element type.

The Drag-In Wizard is Grid Elements' own direct-authoring tool. It exposes suitable elements right inside the page layout and lets an editor create one by dragging it straight into the structural area it belongs in, combining selection, creation and placement in a single interaction. Used consistently, it covers that workflow directly, without needing to open the New Content Element Wizard separately, so an editor builds structure and fills it with content in the same place, in one continuous act of authoring rather than two separate steps.

History and Design Background 

Grid Elements did not set out to add a layout feature to content elements. It continued a specific line of thinking about structural authoring that began with the Grid View, Grid Wizard and Backend Layout concepts developed in 2009. Part of that work later entered TYPO3 Core at page level. That continuity is why the extension models structure the way it does, through a dedicated parent-child relation and per-cell restrictions rather than through page placement, see Data Model and Restrictions, Validation & Permissions, and why nesting was never treated as a special case, see Nesting.

The line runs as follows. Work at the TYPO3 UX Week in 2009 produced Grid View, an approach to describing a page's layout as an explicit structure of rows and columns rather than a fixed template, together with the Grid Wizard, the visual tool used to define that structure. Part of that work was carried forward into TYPO3 Core with version 4.5, as Backend Layouts, giving editors a structural, point-and-click way to define how a page's content is arranged.

The same line of thought was then carried forward again, from page level to content level. Grid Elements emerged in 2011 as that continuation, applying the same structural approach to individual content elements instead of whole pages, with rows, columns, cells and restrictions defining their structure and behavior. Its own Grid Wizard carries the visual structure-definition concept forward to content-element level. Grid Elements therefore represents a direct continuation of the same design work that had already shaped Backend Layouts, carried forward from page-level structure to content-element-level structure.

For the fuller narrative behind this, see the Coders.Care article Grid Elements: The Idea.

Grid Elements and EXT:container 

Grid Elements is often described, informally, as one of several interchangeable "grid" or "container" extensions for TYPO3, with b13/container (EXT:container) as the most frequently mentioned alternative. The two extensions' actual code and documentation describe a different scope for each. This page compares them directly, by architecture, data model and integration mechanism, not by age or popularity, so a reader evaluating either one can judge the actual difference in scope.

Everything said here about EXT:container is based on its own README.md and current source (B13\Container\..., inspected at the time of writing); everything said about Grid Elements is based on this codebase, cross-referenced to the detailed pages linked throughout.

A narrower scope, by EXT:container's own description 

EXT:container's own README.md states its scope explicitly:

"We wanted an extension that only does one thing: EXT:container ONLY adds tools to create and render container elements. There are no FlexForms, no permission handling, or custom rendering."

"We wanted an extension in which every grid has its own Content Type (CType), making it as close as possible to TYPO3 Core functionality."

That is a deliberate design: every container definition is its own CType, registered in PHP per project, with a fixed column/colPos layout, and EXT:container's scope covers creating and rendering that structure.

Grid Elements has a broader scope. A Grid Element is not, in its own model, a container implementation with some extra features attached; it is a Structure-first Authoring system, see Core Concepts, in which container behavior is one part of a larger set of capabilities that share the same underlying structure:

Reusable structural definitions One grid layout, written once as TSconfig, reusable across any number of Grid Elements, see Grid Definitions. EXT:container fixes a layout's columns in PHP per CType.
Grid Wizard Visual structure definition tool, reading and writing that same TSconfig, see Grid Wizard.
Structural parent-child relation A maintained relation independent of colPos, see Data Model.
Row and column semantics An explicit two-dimensional structure, not only a flat set of columns, see Data Model.
Nesting Unlimited structural nesting, with no separate setting to enable it, see Nesting.
Restrictions and validation allowed/disallowed/maxitems enforced server side by Grid Elements itself, with no further extension required, see Restrictions, Validation & Permissions.
References "Paste as Reference" lets an existing record participate in more than one structure without duplication, see References.
Unused-element handling Content that loses its column on a layout change is parked, not lost, see Data Model.
Drag-In Wizard Creates and places a new element in a single drag, without a detour through the New Content Element Wizard, see Drag-In Wizard.
FlexForm integration Per-instance configurable behavior on top of the structure, see FlexForm.
List Module integration Optional nested rendering of a container's children in TYPO3's Web > List module, see List Module Integration.
Structural context at rendering time The relation, cell and layout information built during authoring is what rendering reads, see Rendering Architecture.
Configurable DataProcessor One processor, several structural views of the same data (flat, column-grouped, row/column matrix, recursively pre-fetched), see DataProcessing.

Two of these are worth naming concretely, because they are easy to assume are included in a "container extension" and are not, in EXT:container's own case:

  • Restrictions. EXT:container's own README states plainly: "Supports colPos-restrictions if EXT:content_defender is installed." allowed/disallowed/maxitems enforcement is not part of EXT:container itself; it depends on a separate third-party extension, EXT:content_defender (ichhabrecht/content-defender, IchHabRecht\ContentDefender\..., a different vendor from both b13 and Grid Elements Team) being installed alongside it. Grid Elements enforces the same kind of restriction natively, as part of its own DataHandler logic, with no additional extension, see Restrictions, Validation & Permissions.
  • References. Neither EXT:container's README.md nor its current source contains a "paste as reference" or equivalent content-reference mechanism. Achieving that on an EXT:container-based project depends on a further, separate third-party extension, EXT:paste_reference (Composer package ehaerer/paste-reference). That extension's own documentation describes its origin directly: it "brings the extracted functions from gridelements to copy and paste content elements also as reference and not only as copy", extracted from Grid Elements itself into a standalone extension so projects that do not need the rest of Grid Elements can still have that one capability. Grid Elements still provides "Paste as Reference" as one of its own built-in capabilities, unextracted, see References.

Even combined, EXT:container, content_defender and paste_reference do not reach feature parity with Grid Elements: content_defender validates restrictions against an existing structure, and paste_reference creates reference records, but neither one materializes or authors structure. None of the three provides a Drag-In Wizard, Grid Elements' own structural row/column model, or recursive frontend DataProcessing, since all three are specific to how Grid Elements structures and materializes its own data, see Drag-In Wizard and the DataProcessing comparison below. EXT:container implements a deliberately narrower container model, described as such in its own README. It is not a feature-equivalent replacement to Grid Elements' broader Structure-first Authoring architecture.

Is EXT:container "Core native"? 

EXT:container is sometimes described as more "Core native" than Grid Elements. Technically, that label does not apply to either extension: EXT:container is a third-party Composer/TER package (b13/container), not a TYPO3 Core feature, and it integrates with TYPO3 through the same kinds of extension mechanisms Grid Elements and any other TYPO3 extension use.

Mechanism Grid Elements EXT:container
DataHandler hooks Three (processDatamapClass, processCmdmapClass, an unused moveRecordClass registration), see Extension Points. Six $GLOBALS hook registrations in ext_localconf.php (four processCmdmapClass: CommandMapPostProcessingHook, CommandMapBeforeStartHook, DeleteHook, CommandMapAfterFinishHook; two processDatamapClass: DatamapBeforeStartHook, DatamapPreProcessFieldArrayHook).
PSR-14 event listeners Eight, registered in Configuration/Services.yaml, see Extension Points. Nine, via #[AsEventListener] attributes in Classes/Listener/*: BootCompleted, ContentUsedOnPage (IsContentUsedOnPageLayoutEvent, the same event Grid Elements' IsContentUsedOnPageLayoutListener uses), IsReferenceConsideredForDependency (Workspaces), PageContentPreviewRendering and LegacyPageContentPreviewRendering, ManipulateBackendLayoutColPosConfigurationForPage, ModifyNewContentElementWizardItems (the same Core event Grid Elements' own listener of the same purpose uses), PageTsConfig, RecordSummaryForLocalization (see Localization below).
Own events for third-party listeners One (ModifyRecordListElementDataEvent), see Extension Points. Two (BeforeContainerConfigurationIsAppliedEvent, BeforeContainerPreviewIsRendered).
XCLASS / class-replacement overrides One, conditional on the nestingInListModule opt-in: XCLASSing Core's DatabaseRecordList for List module nesting, see List Module Integration. Two, conditional on EXT:content_defender being active: $GLOBALS['TYPO3_CONF_VARS']['SYS']['Objects'] replaces IchHabRecht\ContentDefender's own DataHandler hook classes with EXT:container's versions, the same override mechanism as an XCLASS.

Both extensions integrate with TYPO3's backend through hooks, events and, where necessary, class overrides; neither uses a different, more "native" channel that the other lacks access to. The full breakdown of Grid Elements' own mechanisms is at Extension Points.

What is true, and is a legitimate design choice rather than a claim about Core integration, is that EXT:container's data model stays close to Core's own colPos field: a container child's cell and its page-level placement are the same value, using project-chosen colPos numbers (for example 200, 201 in its own README example) rather than a value TYPO3 Core itself assigns meaning to. Grid Elements makes a different, equally deliberate choice, keeping those concerns separate, see below. A data model choice is a design choice. It does not turn either extension into TYPO3 Core functionality.

The translated-child relation model, see Localization below, is the one place on this page where a "Core-native" comparison is technically meaningful, and it favors Grid Elements rather than EXT:container. Elsewhere on this page, "Core native" does not describe a real architectural distinction between the two extensions.

Data model 

Grid Elements EXT:container
Parent relation tx_gridelements_container, on the child, pointing at the container's uid, see Data Model. tx_container_parent, on the child, pointing at the container's uid.
Cell identifier tx_gridelements_columns, a structural cell identifier kept separate from colPos, see Data Model. colPos, a project-chosen value with no fixed meaning beyond the container's own PHP definition, and simultaneously the value TYPO3 Core's own placement model sees.
Core placement compatibility colPos remains available for Core's own placement and compatibility behavior (-1/-2 sentinels), independent of the structural cell, see Data Model and Data Compatibility Across TYPO3 Versions. Not distinct from the cell identifier; the same field serves both roles.
Child counter tx_gridelements_children, a maintained counter, see Data Model. None; EXT:container's own migration notes state a container "doesn't store the number of its children".

Keeping tx_gridelements_container, tx_gridelements_columns and colPos as three separate concerns, rather than collapsing the cell identifier into colPos, is what lets Grid Elements give colPos its own, Core-facing sentinel values (-1 for a grid child, -2 for an unused element) without that choice ever constraining what a structural cell is allowed to be numbered. This is a different structural data model, not merely a different field naming scheme for the same idea, see Data Model for the full picture.

One further difference is which side of the relation each extension persists. EXT:container stores tx_container_parent only on the child; the Container record itself maintains no reciprocal, parent-side child relation. The relation is one-way, Child → Parent, and retrieving a container's children always means querying tt_content for the records whose tx_container_parent points back at it. This is a characteristic of the relation, not a claim that EXT:container cannot retrieve its own children; Grid Elements' own tx_gridelements_container requires the same kind of query, see Data Model.

Grid Elements additionally models the parent side of that same underlying TYPO3 inline relation, through tx_gridelements_children, a TCA inline field (foreign_table = tt_content, foreign_field = tx_gridelements_container) that also maintains a stored child counter, see Data Model. tx_gridelements_children is not the source of truth for identifying a container's children; that remains tx_gridelements_container, queried the same way regardless. The architectural difference is that Grid Elements models both the child-to-parent and parent-to-child sides of the relation through TYPO3's own inline-relation machinery, where EXT:container persists only the child-to-parent side.

DataProcessing 

Both extensions render through a TYPO3 DataProcessor, described for Grid Elements' own side in full at DataProcessing, but B13\Container\DataProcessing\ContainerProcessor implements one particular container-processing model, not "the" container processing model as such: it exposes four options, contentId, colPos, as and skipRenderingChildContent. Its behavior splits in two:

  • With ``colPos`` explicitly set, it scopes its query to that one area and returns that area's direct children as a flat list under the configured as variable, rendering each one unless told not to.
  • With ``colPos`` left unset, it determines the container's available colPos areas itself and processes all of them within that same single processor execution, exposing one flat array per area as children_<colPos> (children_200, children_201, and so on). This is EXT:container's own documented default behavior, not something a project has to configure through several processor calls; the explicit-colPos form above exists for choosing a different variable name or targeting one area on its own, not because covering several areas otherwise requires one ContainerProcessor entry per area.

What ContainerProcessor never produces, in either form, is one shared structural object spanning several areas at once: it returns as many separate flat arrays as there are areas involved, not a matrix any of them are cells of.

GridElementsTeam\Gridelements\DataProcessing\GridChildrenProcessor approximates that flat shape as one configuration among several of a substantially broader structural processing system. For one explicitly selected Container area, the approximate configuration is:

sortingDirection = ASC
sortingField = sorting
recursive = 0
resolveFlexFormData = 0
resolveChildFlexFormData = 0
resolveBackendLayout = 0
respectColumns = 0
respectRows = 0
Copied!

respectColumns = 0 is the important setting here, not respectColumns = 1. Grid Elements does not use colPos to represent its structural grid column at all, see Data Model. This is a shape approximation, not a scope one: with all structural shaping disabled, GridChildrenProcessor still retrieves the complete direct child set through tx_gridelements_container (unfiltered by cell); respectColumns/respectRows only change how that already-fetched, complete set is shaped afterward, not what the query scopes to. It does not query-scope that set down to one tx_gridelements_columns value the way ContainerProcessor's colPos parameter scopes its own query to one area. The two produce the same flat, ungrouped shape; the Grid Elements result still contains every one of the container's children, not only those of a single selected cell. Narrowing a flat GridChildrenProcessor result down to one specific structural cell is not something any option does directly; that would have to happen in the template instead.

Everything past that approximation is capability ContainerProcessor has no equivalent option for:

  • Recursion. recursive lets GridChildrenProcessor materialize additional levels of a nested Grid Element tree into one resulting data structure, within one DataProcessing result. It does not do this in a single database query for the whole tree: for each child that is itself a Grid Element, the processor invokes a further GridChildrenProcessor run against that child, with recursive decremented by one, which issues its own separate query for that level. The materialized result nests as one tree in the template; reaching it still costs one additional processor call and query per nested level, not one query overall. ContainerProcessor processes only the current container's direct children; a nested container among those children is, to it, just another content element, processed and rendered separately when its own turn comes, not a claim that EXT:container's containers cannot structurally nest.
  • Rows. Grid Elements' structural model has an explicit row dimension, not only columns, see Data Model. respectRows can preserve that dimension in the output, indexing children by row first, with the column-indexed arrays nested inside. ContainerProcessor has no equivalent, since its underlying data model does not carry a row concept separate from colPos.
  • Columns. respectColumns, enabled, retrieves the full direct child set through tx_gridelements_container and reshapes it into one matrix indexed by tx_gridelements_columns, across all of a container's cells, in a single processing operation and a single output variable. This is the capability behind the data model distinction above turning into a different processing capability, not merely a different field name.
  • Backend Layout and FlexForm resolution. resolveBackendLayout resolves the current Grid Element's (and, for nested children, their own) Backend Layout information; resolveFlexFormData and resolveChildFlexFormData resolve FlexForm data for the current Grid Element and its children respectively, see FlexForm. EXT:container's own scope explicitly excludes FlexForm handling ("There are no FlexForms..."), consistent with, not a gap relative to, its stated design.
  • Configurable sorting, sortingField and sortingDirection, rather than only the fixed common case.

See DataProcessing for the full option reference and how to configure GridChildrenProcessor in a project.

Localization 

Both extensions build on TYPO3 Core's connected/free localization modes, described for Grid Elements' own side in full at Localization, not on a localization mechanism of their own. Where they differ architecturally is what each keeps its structural relation pointing at once a child is translated, and this is the one place on this page where a "Core-native" comparison is technically meaningful, not because either extension is Core, but because one relation model follows the pattern TYPO3 Core's own translated inline relations are expected to have, and the other deliberately does not.

Grid Elements' own DataHandler logic (checkAndUpdateTranslatedElements(), see Parent-Child Relations and IRRE) resolves a record's translated counterpart the same way TYPO3 Core itself would, through l18n_parent, and keeps a translated child's tx_gridelements_container pointing at the corresponding translated container found that way, not at the original-language one. This is exactly the relation shape an ordinary TYPO3 inline relation between a translated child and a translated parent is expected to have; the structural parent-child relation stays valid, in the ordinary Core sense, inside the translated language tree.

EXT:container's own documentation describes a different model for its connected mode: translating a container also translates its children, a translated child's l18n_parent points, as usual, to its translation origin, but its tx_container_parent points to the original-language container, not the translated one, a relation Core's own model would not itself produce. Because the relation field does not follow the translation, EXT:container's current code carries additional localization-specific logic to reinterpret those records correctly wherever that matters, including its ContentUsedOnPage and RecordSummaryForLocalization event listeners, see Extension Points.

The distinction is not merely that the two extensions "handle localization differently": Grid Elements follows TYPO3 Core's normal translated inline-relation model, while EXT:container introduces its own cross-language parent relation and then has to compensate for it through additional overlay and localization-specific logic. Public issues document this causing real integration friction: l10nmgr, which imports translated content through TYPO3's normal record-translation workflow, ran into this relation model (b13/container#193), and the same relation model is documented as causing a separate problem within EXT:container's own Connected Mode translation (b13/container#609). DeepL-based translation integrations have also required compatibility fixes for EXT:container, as documented in the deepltranslate_core changelog (localization wizard with EXT:container and an earlier fix for translation of inline elements in containers). The changelog documents that such fixes were required; it does not itself attribute them to the cross-language tx_container_parent relation specifically.

When each fits 

EXT:container is a coherent choice when a project's need is exactly what it describes itself as solving: a small, fixed set of per-project container layouts, each its own CType, rendered through ContainerProcessor's flat, area-based processing model, with no requirement for reusable structural definitions, restrictions without an additional extension, references, unused-element handling, or one-motion drag-in authoring. Grid Elements fits when some or all of the broader capability list above is what a project actually needs; the two are not competing implementations of the same scope, and the choice between them should follow from that difference in scope, not from which one is perceived as newer or more widely discussed.

Further reading 

Grid Elements 14 Available – Version 13 in the TER, a Coders.Care article making the same distinction from the maintainer side: "Gridelements is far more than a solution for simple container structures. The extension follows an independent and comprehensive approach that combines flexible layouts, editorial control and comfortable visual handling."

Grid Elements: The Idea, the Coders.Care article on Grid Elements' own conceptual background, also cited in Introduction, for the broader argument that Grid Elements "was never really about grids" in the narrow sense a container-only extension covers.

Core Concepts The Structure-first Authoring model this comparison assumes.
Data Model The full tx_gridelements_container / tx_gridelements_columns / colPos picture.
Nesting How structural nesting works, independent of any single DataProcessor option.
DataProcessing The detailed GridChildrenProcessor vs. ContainerProcessor comparison.
Localization The detailed localization relation comparison.
Extension Points The full mechanism-by-mechanism integration comparison.

Getting Started 

This section takes a project from an empty installation to a working Grid Element rendered in the frontend.

Installation Get the extension into a project, activate it, and put the minimum configuration in place.
Quick Start Define a small grid structure, create it in the Page Module, fill it with content and see it render.

Both pages assume a working TYPO3 installation and a project comfortable editing TSconfig and TypoScript. Neither one is a reference manual. Once the first Grid Element is up and running, Grid Definitions documents the full grid structure syntax, Editor Guide covers day-to-day authoring, and Rendering covers the frontend output in depth.

Installation 

Getting Grid Elements ready for use has three parts: get the extension into the project, update the database schema for its tables and fields, and include the TypoScript that renders its output. All three are one-time, project-level setup.

Install with Composer 

Composer is the primary and recommended way to install Grid Elements. From the project root:

composer require gridelementsteam/gridelements
Copied!

In a Composer-managed TYPO3 installation, this alone makes the extension active. There is no separate activation step in the Extension Manager; the Extension Manager, in Composer mode, simply reflects what is required in composer.json.

Update the database schema 

Grid Elements adds its own database table for CE backend layout records and a handful of fields to tt_content. Bring the schema up to date with either:

vendor/bin/typo3 database:updateschema
Copied!

or the Install Tool's Analyze Database Structure function under Admin Tools > Maintenance. Review the proposed changes and apply them.

Dependencies 

The current Composer package requires PHP 8.2, 8.3 or 8.4, and TYPO3 12.4 LTS or 13.4 LTS (patch level 13.4.7 or later). It has no dependency on any other third-party extension. Consult composer.json in the package for the exact constraint, and the version matrix in the project's README for how this maps to other Grid Elements versions.

Basic configuration 

With the extension active and the schema updated, one piece of frontend configuration remains before a Grid Element can render: including its TypoScript. This is the only step that still needs doing manually; everything about defining a grid's structure is TSconfig, covered in Grid Definitions, and does not require touching TypoScript at all.

Grid definitions versus rendering configuration 

Keep the two configuration languages apart. A grid's rows, columns, cells and restrictions are TSconfig, not TypoScript; Grid TS Syntax documents that notation, and the Grid Wizard builds it visually. TypoScript, the static template just included, is only ever about frontend rendering output. Neither one substitutes for the other.

Installing without Composer 

Grid Elements is also published in the TER for installations that do not manage extensions with Composer. Download or import it there, then activate it in the Extension Manager as with any other classic-mode extension. The database schema update and TypoScript inclusion steps above are the same either way.

Historical note. Grid Elements' extension metadata still declares a conflict with TemplaVoila. This declaration dates from an earlier period when the two extensions shared some of the same TYPO3 Core hooks and could not be active on the same installation at once; it remains in the metadata as a leftover from that situation, not as a warning specific to any project built today.


With the extension installed, activated and its static template included, continue to Quick Start to define and render a first Grid Element.

Quick Start 

This walkthrough builds one small, real Grid Element: a two-area structure named Main and Sidebar. It follows the same sequence any Grid Element goes through: define a structure, instantiate it, place content in its areas, and render it. That sequence is Structure-first Authoring, the authoring model behind Grid Elements; this page demonstrates it rather than explains it, see Core Concepts for the concept itself.

Define the grid layout 

A grid's structure is TSconfig, not TypoScript, see TSconfig. Add the following to the page TSconfig of the page tree this project works in, either the site's page.tsconfig file or, for quick experimentation, the Resources tab of a page's properties:

tx_gridelements.setup.GridElement {
  title = Two-column layout
  config {
    colCount = 2
    rowCount = 1
    rows {
      1 {
        columns {
          1 {
            name = Main
            colPos = 0
          }
          2 {
            name = Sidebar
            colPos = 1
          }
        }
      }
    }
  }
}
Copied!

title is what identifies this layout to editors in the backend, it must be set or the layout will not appear as a choice at all. colPos here is a cell identifier local to this layout, not TYPO3 Core's own colPos field, see Grid TS Syntax for the full notation and Data Model for what actually relates a child to its container. GridElement, the identifier this layout is defined under, is explained below, in See it render.

This is the same structure the Grid Wizard would produce visually on a CE backend layout database record; either route defines the same thing, see Grid Definitions for both.

Create the Grid Element 

Reload the backend so the new layout is picked up, then open the Page Module on a page within reach of that TSconfig. Use the Drag-In Wizard, the toggle button next to the Page Module's own Create new content element button, find Two-column layout among the Grid Elements offered there, and drag it into a column. TYPO3's standard New Content Element Wizard offers the same layout and works just as well; either one creates a Grid Element with this structure, already placed on the page.

Fill the structural areas 

The new Grid Element now shows Main and Sidebar as two areas on the page, each with its own Content control, exactly like an ordinary page column:

A Grid Element's structural areas in the Page Module

Add a content element, a simple Text element is enough, to Main, and another to Sidebar, the same way content is created anywhere else in the Page Module.

See it render 

Preview the page in the frontend. Both areas already appear, each showing the content just placed in it, with no template written for this walkthrough.

That works because of how the recommended static template resolves a template file: it looks up a Fluid template named after the grid layout's own identifier, tx_gridelements.setup.<id>. Grid Elements ships one ready-made, generic template under exactly that lookup mechanism, GridElement.html, built to render any row and column structure without per-layout customization. Naming this walkthrough's layout identifier GridElement is what makes it resolve straight to that shipped template, so there is nothing left to configure for a first render. A real project typically gives each layout its own identifier and, where its rendering should differ, its own template, see Rendering Architecture and How To for how that resolution works and how to build on it.

Where to continue 

Grid Definitions The full Grid TS Syntax notation, the Grid Wizard, FlexForm and every TSconfig option used to build a layout like this one.
Restrictions, Validation & Permissions Limit which content types, and how many, a cell accepts.
Editor Guide Day-to-day authoring: the Page Module, drag and drop, both wizards, references and localization.
Rendering The rendering architecture, GridChildrenProcessor in depth, and its full option reference.
Data Model The parent-child relation, nesting and the data actually stored behind what was just built.

Editor Guide 

The Page Module is where a Grid Element's structure becomes something an editor actually works with. Its rows, columns and cells show up as real areas on the page, and each one behaves like any other column an editor already knows how to use: content can be created in it, moved into it, or removed from it, right where it belongs.

This guide covers that everyday work. An editor can place content directly in the structural area it belongs to, move existing content between areas, nest one Grid Element inside another to build more elaborate layouts, and bring in content that already exists elsewhere by referencing it instead of duplicating it. Restrictions configured on a cell narrow down what can be placed there, so the structure itself steers an editor toward a valid result instead of leaving the rules to memory.

Two tools cover most day-to-day content creation inside a grid. TYPO3's standard New Content Element Wizard remains available, exactly as on any page column. Grid Elements also provides its own Drag-In Wizard, which lets an editor pick an element and place it in one motion, without a detour through a separate dialog. Used consistently, the Drag-In Wizard covers that workflow directly.

Where to go next 

Working with Grid Elements in the Page Module How a Grid Element's structure appears in the Page Module, and what an editor can do with it directly.
Drag and Drop Moving content that already exists between columns, cells and nested structures.
Drag-In Wizard Creating and placing a new element in a single interaction, directly inside the structural area it belongs to.
New Content Element Wizard TYPO3's standard element-creation dialog, and how Grid Elements fits into it.
References Letting an existing content record appear in more than one structure without duplicating it.
Localization What happens to a Grid Element and its children when a page is translated.

Working with Grid Elements in the Page Module 

A Grid Element shows up in the Page Module as a block with its own rows and columns, drawn the same way TYPO3 draws a page's own Backend Layout, only scoped to that one element instead of the whole page. Each cell of that block is a real, visible area: it shows the content it already holds, and it carries an add content control of its own, so a new element can be created directly inside it.

Structural areas 

The rows and columns a layout defines (see Grid Definitions) become the cells an editor actually sees and works in. Placing content in a cell is no different from placing it in any page column: the element's edit, hide and delete controls behave the same way, and the cell shows exactly the content assigned to it.

A Grid Element with several structural areas in the Page Module

A Grid Element's own rows and columns, each with its own content and its own add content control.

Nested Grid Elements 

A cell's content is not limited to plain content elements. Placing a Grid Element inside another Grid Element's cell nests one structure inside another, and the nested element draws its own rows and columns the same way, right there inside the parent's cell. There is no depth limit and no separate setting to enable this: it works because a child can be any content element type, including another Grid Element, see Nesting for the underlying model. An editor builds a nested structure simply by placing a Grid Element where any other content element could go.

Restrictions guide what belongs where 

A cell can be configured to accept only certain kinds of content, or to accept no more than a certain number of elements. Where that is the case, the choices offered to an editor, in the New Content Element Wizard, the Drag-In Wizard, and as valid drop targets while dragging, are already narrowed to what is actually allowed, and TYPO3 also refuses anything that would violate those rules even if it were somehow submitted anyway. An editor does not need to know a cell's exact rules to work within them: an area that only accepts text elements simply will not offer anything else. See Restrictions, Validation & Permissions for the full picture, including how these rules are configured and enforced.

Behind the placement: colPos and unused elements 

Day to day, an editor never needs to think about how a child's position is stored. It is worth knowing that if a layout changes so that a cell an element used to occupy no longer exists, the element is not lost. It reappears in an unused elements area instead, wherever a page's own backend layout provides one, and is restored to its original cell automatically if a later layout change brings that cell back. See Data Model for how this is actually stored.

Grid Elements in the List Module 

Where an installation has opted into it, the List Module lists a Grid Element's children nested underneath their container, with per-user expand and collapse state, instead of mixed into a flat list of the page's content. This is a per-installation setting, not something an editor turns on or off. If a project has not enabled it, a Grid Element's children still show up in the List Module, just as ordinary tt_content rows without that nested grouping. The mechanism itself belongs to the Developer Reference.

Drag and Drop 

Drag and drop moves an element that already exists. It is TYPO3's own Page Module mechanism: grab a content element by its header and drop it where it should go. Grid Elements extends this mechanism so it also works across a grid's own cells and into and out of nested structures, honoring the same restrictions that govern any other way of placing content there.

This is a different action from the Drag-In Wizard. Drag and drop always moves or copies a content element that is already on the page. Creating a brand new element by dragging its type directly into a structural area is the Drag-In Wizard's job, not ordinary drag and drop, even though both interactions look similar at a glance.

Moving within a Grid Element 

Dragging a child from one cell of a Grid Element to another cell of the same Grid Element relocates it there. Only cells that would actually accept the dragged element highlight as valid targets while dragging; a cell whose restrictions rule out that element's type simply never lights up, so an invalid drop is not offered in the first place rather than rejected afterwards.

Moving between structural areas 

The same drag works between a page column and a Grid Element's cell in either direction, and between the cells of two different Grid Elements on the same page, including cells of a Grid Element nested inside another one. Dropping an element into a plain page column detaches it from any Grid Element it used to belong to; it becomes an ordinary page-level element again, placed in that column like any other. See Data Model if the underlying field changes that make this possible are of interest.

Copy instead of move 

Holding down Ctrl while dropping (Option on macOS) copies the element to the target cell instead of moving it, leaving the original in place. This is the same modifier TYPO3 uses for drag and drop elsewhere in the Page Module.

Restrictions decide what is offered 

A cell's allowed, disallowed and maxitems configuration is checked while dragging, not only after dropping. Cells that would reject the dragged element's type, or that have already reached their maximum number of elements, do not become active drop targets at all. See Restrictions, Validation & Permissions for how these rules are configured and how they are enforced again, independently, once a drop is submitted.

Drag-In Wizard 

The Drag-In Wizard is Grid Elements' own direct-authoring tool. It puts the same element types that the New Content Element Wizard offers directly inside the Page Module, as a panel an editor can drag items out of, and turns creating an element into a single motion: pick an item, drag it into the structural area it belongs in, and it exists there, already placed, the moment it is dropped.

This is the practical core of Structure-first Authoring. An editor builds a page's structure and fills it with content in the same place, in one continuous act, rather than switching between a separate creation dialog and the layout it is building.

Opening the wizard 

A toggle button next to the Page Module's own Create new content element button opens and closes the wizard panel. Once open, it stays available while an editor keeps working, so it can be left open for a whole session of building out a page rather than reopened for every element.

What the wizard presents 

The panel lists the same categories and element types the New Content Element Wizard would offer for the current page, grouped the same way, with the same icons and descriptions. Nothing needs to be configured twice: the Drag-In Wizard is a different way of reaching the same set of choices, not a separate catalogue to maintain.

What the panel actually shows is pre-filtered before any drag begins. It examines every column currently rendered on the page, page-level Backend Layout columns and any Grid Element's own cells alike, and offers an element type only if at least one of those columns would accept it under its restriction configuration, see Restrictions, Validation & Permissions. An element type that no column on the current page allows does not appear in the panel at all; an editor is never offered something that has nowhere to go on the page being edited. This pre-filtering of what is offered is a separate mechanism from what happens once a drag is already in progress, described below.

Creating an element by dragging it in 

Dragging an item out of the panel highlights the structural areas on the page that would actually accept it, exactly as ordinary Drag and Drop highlights valid targets for an existing element. Dropping it there creates a new element of that type, already placed in that column, in that Grid Element's cell, or nested inside another Grid Element, wherever it was dropped. Selection, creation and placement happen as one interaction, so there is no intermediate step where the new element exists but has not been placed yet.

How restrictions shape what is offered 

Restrictions act at two separate points, not just one. Before a drag starts, they decide which element types appear in the panel at all, see above. Once a drag is in progress, the same restriction configuration decides which columns and cells light up as valid drop targets for the specific item being dragged: only the columns and cells that would actually accept that item's type light up. A cell restricted to a particular set of content types, or one that has already reached its maximum number of elements, simply does not become a drop target for anything it would reject. This is the same restriction configuration described in Restrictions, Validation & Permissions, applied here as it is applied to any other way of placing content.

Why one interaction instead of two 

Without the Drag-In Wizard, adding content to a specific cell means opening the New Content Element Wizard from that cell's own create new content element control, choosing a type, and waiting for the dialog to return to the page. The Drag-In Wizard removes that detour: the wizard is already open, alongside the structure it will fill, and placing an element is the same motion as choosing it. Used consistently, it covers ordinary element creation directly, so an editor building out a page's structure does not need to open the New Content Element Wizard, described next, for that part of the work.

New Content Element Wizard 

The New Content Element Wizard is TYPO3 Core's standard dialog for choosing and creating a content element. It opens from the create new content element control on a page column or on a Grid Element's own cell, and remains exactly what it has always been: a categorized list of element types an editor picks from.

Grid Elements integrates with this dialog rather than replacing it.

Where Grid Elements appears in it 

Opened from a column or cell that allows Grid Elements at all, the wizard's list includes the Grid Element layouts available there, alongside TYPO3's own content element types and any installed plugins. Picking one creates a new Grid Element of that layout, already placed in the column or cell the wizard was opened from.

How structural context narrows the choices 

The wizard's list is not the same everywhere. Which content element types, plugins and Grid Element layouts appear depends on the restrictions configured for the column or cell the wizard was opened from. An area limited to a small set of layouts, or one that disallows Grid Elements entirely, simply does not offer the choices it disallows. This is the same restriction configuration described in Restrictions, Validation & Permissions, applied here as it is applied everywhere else an editor creates or places content.

Relationship to the Drag-In Wizard 

The New Content Element Wizard and the Drag-In Wizard are two different ways of reaching the same choices, not competing workflows. The New Content Element Wizard remains a valid, ordinary way to create content, opened from wherever it is needed, one element at a time. An editor who uses the Drag-In Wizard consistently while building out a page's structure may simply never need to open this dialog for that part of the work, since the Drag-In Wizard covers element creation directly from the same panel. Both remain available side by side; neither is deprecated in favor of the other.

References 

A reference lets an existing content record appear in another structure without being duplicated as an independent record. The content shown is the same content, wherever it appears; there is only ever one record to edit.

Creating a reference 

Referencing starts the same way copying does: an element is put on the clipboard from its context menu, on this page or on another one. From there, an editor has two ways to bring it in as a reference rather than a copy:

  • From an existing element's own context menu, once something is on the clipboard in copy mode, Paste reference after appears right next to the ordinary Paste after action.
  • From an empty cell or column's create new content element control, a Paste dialog appears when something is on the clipboard, offering Paste and Paste reference side by side.

Either way, the target cell or column, including a Grid Element's own cell, is honored, so a reference lands exactly where an ordinary paste would.

What is actually referenced 

A reference is its own, small content record. It does not hold a copy of the referenced content; it holds a pointer to it. What an editor sees in the Page Module is the referenced content's own preview, rendered in place and marked with a border so it reads clearly as a reference rather than as the content element itself. Editing the referenced content is done by opening the original record, wherever it lives; the reference itself has nothing of its own to edit beyond where it points.

Because a reference is a pointer, not a duplicate, editing the original updates every place that references it. This is the meaningful difference from a normal copy: a copy is independent from the moment it is created, while a reference stays connected to a single source of truth.

Creating a reference requires the same backend-user permission TYPO3 uses for the shortcut content element type, since a reference is implemented as one. See the permissions section of Restrictions, Validation & Permissions for how that is enforced; an editor without that permission simply does not see the Paste reference choice.

Nesting and restrictions 

A reference can be placed inside a Grid Element's cell exactly like any other content element, including a cell of a nested Grid Element. Because a reference is technically a shortcut content element, the column or cell restrictions that would allow or disallow a shortcut element apply to whether a reference can be pasted there at all, the same way any other restriction narrows down what a cell accepts, see Restrictions, Validation & Permissions.

Localization 

Translating a page's content works the same way for a Grid Element as for any other content element, because the mechanism itself is TYPO3 Core's, not something Grid Elements replaces or adds to. What is worth understanding is how that mechanism, connected or free localization, interacts with a Grid Element's own structure and its children.

Translate versus Copy 

When translating a content element, TYPO3 offers two modes unless a project has explicitly opted into mixing them on the same page (mod.web_layout.allowInconsistentLanguageHandling in Page TSconfig):

  • Translate creates connected records: each translation stays linked to its default-language source. Once a language column holds connected records, TYPO3 locks that column down: no new element can be created there, nothing can be pasted into it, and nothing can be dragged into or within it.
  • Copy creates free records: independent translations with no link back to a source. A column of free records behaves exactly like a default-language column, with full drag and drop, paste, and new-element creation.

This choice is made once, per page and language, through TYPO3's own translation tooling; it is not a Grid Elements setting, and Grid Elements does not override or replace it. The lock on a connected column is TYPO3 Core's own, applied to the column as a whole, and it governs a Grid Element's own cells exactly as it governs a page column: an editor who needs to rearrange a Grid Element's children in a translated language needs that language in free (Copy) mode; a connected (Translate) language keeps the structure locked to whatever the default language already has.

Translating a Grid Element and its children 

A Grid Element's children are ordinary content records related to it through a dedicated parent-child relation, not through their position on the page, see Data Model. Nothing about that relation is specific to one language: a Grid Element and each of its children have their own translation state, exactly as any other content element would.

Two places in the backend deal with translating a Grid Element's children:

  • The container's own edit form lists its children and offers TYPO3's usual localization controls there, letting an editor localize or synchronize individual children directly from the container.
  • When a whole page's content is translated at once through TYPO3's page-wide translation tool, a Grid Element and its children are offered as a single unit rather than as separate top-level entries; a child already accounted for through its container is not listed again on its own. An editor does not need to individually select a container's children in that tool for them to be included.

Because none of this depends on any Grid Elements-specific TCA override, and both mechanisms build directly on TYPO3 Core's own inline-relation and page-translation handling, this behavior can be relied on across TYPO3 versions that support Grid Elements' underlying requirements. It is not something a future TYPO3 upgrade is expected to change without a corresponding Core change to connected/free localization itself.

For the architectural difference between Grid Elements and EXT:container localization, including why Grid Elements' relation model is one of the few places "Core-native" is a technically meaningful description, see Grid Elements and EXT:container.

Grid Definitions 

A Grid Element's rows, columns and cells do not exist until an integrator defines them. This section covers how that definition happens: the syntax that describes a grid's structure, the visual tool that authors it, the additional properties a grid layout can carry through FlexForm, and the TSconfig options that tie all of it together.

This is definition-time configuration. It determines what structural areas exist and what may be placed in them. It does not cover how editors fill those areas with content, which is the Editor Guide, or how the resulting structure reaches the frontend, see Rendering.

How the pieces fit together 

Grid structure (rows, columns, cells, restrictions)
    |
    +-- written as Grid TS Syntax ............ see Grid TS Syntax
    |
    +-- authored visually with the Grid Wizard  see Grid Wizard
    |       (the wizard reads and writes the same TSconfig)
    |
    +-- delivered to TYPO3 as TSconfig ........ see TSconfig
    |
    +-- optionally extended with configurable
        properties through FlexForm ........... see FlexForm
Copied!

The Grid Wizard and Grid TS Syntax describe the same structural concept from two directions, one visual, one textual, not two unrelated configuration systems. A layout built with the wizard is TSconfig; TSconfig typed by hand opens in the wizard as the same structure. FlexForm configuration is a separate, optional layer: it adds configurable properties to a grid layout, it does not itself define rows, columns or the parent-child relation, see Data Model for how that relation is actually stored.

Because a grid definition is TSconfig, it is plain text: it can live in a project's version control alongside the rest of its configuration, be diffed and reviewed like code, and be reused across backend layouts or projects without re-entering it by hand.

Grid TS Syntax 

Grid TS Syntax is the notation used to describe a grid's rows, columns and cell configuration. It is backend structural configuration, part of TSconfig, not frontend TypoScript. A page's Backend Layout and a Grid Element's own layout both use the same syntax, parsed by the same internal TypoScript parser Core uses elsewhere, which is why the notation itself looks like TypoScript even though its purpose here is purely structural.

You can write this syntax by hand, or build it visually with the Grid Wizard, which reads and writes the exact same structure. Once a project has several similar layouts, editing the syntax directly is often faster than repeating the same wizard clicks.

Step by step 

Start with the number of columns and rows 

Use the keys colCount and rowCount to define the grid a layout's cells are placed on. Both values should be at least the lowest common multiple of the column sizes you intend to create, since they describe the grid the cell structure is measured against, and the calculation should account for any cells that will span multiple columns or rows.

config {
  colCount = 4
  rowCount = 3
}
Copied!

Fill in the rows 

rows is a plain array with one numeric key per row, even a row that will stay empty:

config {
  colCount = 4
  rowCount = 3
  rows {
    1 {
      ...
    }
    2 {
      ...
    }
    3 {
      ...
    }
  }
}
Copied!

Create the cells 

Each cell in a row's columns array accepts up to seven keys: name, colPos, colspan, rowspan, allowed, disallowed and maxitems. name is required. Without a value for colPos, the cell has no target column and stays inactive, a placeholder in the page module that cannot hold content.

colspan, rowspan, allowed, disallowed and maxitems are all optional.

colPos here is a cell identifier local to this layout, not TYPO3 Core's own colPos field on a content element. It becomes the value stored in a child's tx_gridelements_columns once it is placed in that cell. It does not create the parent-child relation between a container and its children; that relation, and why a child's own colPos instead carries a sentinel value, is documented in Data Model.

allowed, disallowed and maxitems define, per cell, which content is permitted and how many elements a cell accepts. allowed and disallowed take an array with CType, list_type and tx_gridelements_backend_layout as keys, restricting content element types, plugin types and nested grid element types respectively; disallowed wins where the two overlap. This syntax matches the one used by the third-party content defender extension, so a project already using it does not need to change its configuration. maxitems caps how many elements the cell accepts; a cell at or over its limit still shows existing elements and stays editable, it only stops offering more. This is definition-time configuration; how it is enforced and filtered at authoring time is covered fully in Restrictions, Validation & Permissions.

The following example defines a larger top row, an outer/inner left column pair, a right column with an item limit, and an outer right placeholder-free column:

config {
  colCount = 4
  rowCount = 3
  rows {
    1 {
      columns {
        1 {
          name = Top
          colspan = 4
          colPos = 0
          allowed {
            CType = text,textpic
          }
        }
      }
    }
    2 {
      columns {
        1 {
          name = Outer Left
          rowspan = 2
          colPos = 1
          allowed {
            CType = text,textpic
            tx_gridelements_backend_layout = 2ColumnContainer,3ColumnContainer
          }
        }
        2 {
          name = Left
          colPos = 2
          allowed = *
          disallowed {
            CType = text,textpic
            tx_gridelements_backend_layout = 2ColumnContainer,3ColumnContainer
          }
        }
        3 {
          name = Right
          colPos = 3
          maxitems = 4
        }
        4 {
          name = Outer Right
          colPos = 4
        }
      }
    }
    3 {
      columns {
        1 {
          name = Bottom
          colspan = 4
          colPos = 5
        }
      }
    }
  }
}
Copied!

This is the visible result of the example code:

Result of example code

Once a Grid Element using this layout has children, editing it shows them connected to their parent through Core's Inline Relational Record Editing (IRRE), see Parent-Child Relations and IRRE for how that relation is maintained. Nested grids and their own children can be edited the same way, without leaving the form, though sorting by drag and drop or the sorting arrows is disabled inside it; dragging and dropping elements directly in the page module still works.

Edit page content

Grid Wizard 

The Grid Wizard is a visual tool for defining a grid's rows, columns and their relationships, spanning, restrictions and column numbers, without typing Grid TS Syntax by hand. TYPO3 Core provides the same tool for page-level Backend Layouts; Grid Elements carries it forward to content-element level, see History and Design Background for how that continuity came about.

The Grid Wizard belongs to the structure-definition layer described in Core Concepts. It defines a structural area; it does not create content. It is not the New Content Element Wizard, which selects and creates a content element, and it is not the Drag-In Wizard, Grid Elements' own direct-authoring tool for creating and placing content in one interaction. Using the Grid Wizard on a layout changes the structure that layout offers, not the content already placed inside a Grid Element using it, directly. If a cell or column that already holds child elements disappears from the layout definition, though, Grid Elements' consistency handling can move the affected children into the unused state; should a matching cell become available again later, those children can become available again accordingly, see Restrictions, Validation & Permissions and the Editor Guide's material on unused elements for how that handling works.

Creating the basic grid structure 

Open the Configuration tab of a CE backend layout record to find the wizard within the editing form. On a new, unconfigured record, it starts empty:

Create basic grid structure step 1

For an existing record, it instead renders the structure already present in the layout's TSconfig.

Use the small arrows on the right and bottom edges to size the grid: right and down add columns or rows, left and up remove them. Building the same structure used in the Grid TS Syntax example produces this basic grid:

Create basic grid structure step 2

Spanning, naming and assigning cells 

Click the triangle symbols beside a cell to have it span additional columns or rows. Spanning is only possible to the right and down, matching how cells are spanned in the HTML table the page module renders. Once a cell spans at least one extra column or row, additional triangles pointing left and up appear, letting you undo the spanning the same way.

To reproduce the Grid TS Syntax example: span the upper left cell's first row triangle until it covers the whole row, span the first cell of the second row downward so it covers two rows, then span the second cell of the last row rightward until it covers the remaining three columns:

Create basic grid structure step 3

Each cell still needs a name and, if it should hold content, a column number, which becomes that cell's colPos in the resulting Grid TS Syntax, see Grid TS Syntax for what that value does and does not establish. This is also where you set the allowed and disallowed content, list and grid element types and the maximum number of items for the cell, see Restrictions, Validation & Permissions for how those values are enforced. A cell left without a column number stays a placeholder and cannot hold content later on. Click the pencil icon in the middle of a cell to edit its values, and the disk icon to save them:

Create basic grid structure step 4

Saving the layout to the CE backend layout record 

With every cell named and assigned, the layout is complete:

Create basic grid structure step 5

Saving the record converts the visual structure into the same Grid TS Syntax shown in that page's example. Reopening the wizard later restores it in the same visual state, so the two representations, visual and textual, always describe the same structure and can be moved between freely.

The result does not have to stay a database record. Copy the generated syntax out of the wizard's TSconfig display and paste it into a TSconfig file under tx_gridelements.setup, which also makes it reusable and versionable across projects. A layout defined this way reaches a page tree through TYPO3's normal Page TSconfig loading and inheritance, the same as any other Page TSconfig; no separate option is needed to point a page tree at it. See TSconfig for that option and the further keys it accepts, and its PAGE_TSCONFIG_ID option, which serves a different purpose: pointing at the page that acts as the storage folder for CE backend layout database records.

FlexForm 

A grid definition, written as Grid TS Syntax or built with the Grid Wizard, describes a layout's rows, columns and restrictions. It does not describe any configurable behaviour a Grid Element using that layout should offer beyond its structure, options an editor sets once per Grid Element instance, such as a display variant or a note for other editors. That is what the FlexForm Configuration field on a CE backend layout record is for.

FlexForm configuration is additive, not structural. It never defines rows, columns or restrictions, and it plays no part in the parent-child relation described in Data Model. A Grid Element with no FlexForm configuration at all still fully supports children, nesting and restrictions; FlexForm only adds fields to that element's own editing form and, from there, to its own data.

What to put in a FlexForm 

Anything unrelated to the relation between a container and its children can go in a Grid Element's FlexForm configuration, using the same syntax as any other FlexForm field:

  • Checkboxes or radio buttons to toggle behaviour.
  • Selectors to pick a rendering variant of the Grid Element in the frontend.
  • Input fields for additional configuration values.
  • Text areas for internal notes to other editors.

Everything defined in the data structure shows up as fields in the Grid Element's own edit form, under Content Element Configuration.

FlexForm data structures use the same XML format as elsewhere in TYPO3, including the format TemplaVoila-based content elements use, which can make copying an existing data structure a useful starting point when migrating such elements to Grid Elements.

How a layout's data structure is resolved 

A Grid Element's data structure is resolved per grid layout, not globally. On a tt_content record with CType = gridelements_pi1, the extension's BeforeFlexFormDataStructureIdentifierInitializedListener and BeforeFlexFormDataStructureParsedListener PSR-14 listeners look up the record's selected layout through LayoutSetup, the same shared parsing layer rendering depends on, and resolve its data structure from one of two CE backend layout record fields:

  • pi_flexform_ds_file, a data structure referenced as a file, either a TYPO3 file reference or a plain file path.
  • pi_flexform_ds, a data structure entered directly as XML.

pi_flexform_ds_file takes precedence when both are set. A layout defined purely through TSconfig, without any CE backend layout record, can set the same value using the flexformDS key under tx_gridelements.setup.<id>, see TSconfig; LayoutSetup maps it onto pi_flexform_ds internally, so both paths resolve the same way.

Reading FlexForm values in the frontend 

When resolving FlexForm data is enabled, both rendering paths, see Rendering Architecture, convert a Grid Element's pi_flexform value into individual fields, each prefixed with flexform_ so it cannot collide with any of the record's own field names, and make them available on the record like any other field. In the recommended DataProcessing path this happens through GridChildrenProcessor's resolveFlexFormData and resolveChildFlexFormData options, documented in Reference.

TSconfig 

Grid definitions live in TSconfig. Grid TS Syntax documents the notation itself, rows, columns, cells and their restrictions; this page documents the TSconfig options around that notation: where a grid layout's configuration is looked up, how several sources of layouts combine, and how a page tree is pointed at the page holding them. It is not a general introduction to TYPO3 TSconfig.

tx_gridelements 

tx_gridelements.setup 

Property
tx_gridelements.setup
Data type
Grid TS structure
Description
Container for one or more grid layout definitions, each keyed by a layout ID under tx_gridelements.setup.<id>.
Default
N/A

tx_gridelements.setup.<id> 

Property
tx_gridelements.setup.<id>
Data type
Grid TS structure, plus the options below
Description
Defines a grid layout entirely through TSconfig, without a CE backend layout database record. <id> is the layout identifier a Grid Element's own tx_gridelements_backend_layout field selects. Build the structure with the Grid Wizard and copy it here, or write it by hand following Grid TS Syntax. A layout defined this way and a layout stored as a database record can coexist under the same ID, see overruleRecords below for which one wins.
Default
N/A

Alongside the grid structure itself (tx_gridelements.setup.<id>.config), a TSconfig-defined layout accepts a small number of further keys, mirroring fields available on a CE backend layout database record:

Key Role
config The layout's own Grid TS Syntax (colCount, rowCount, rows, and so on).
icon One or more icon references shown for this layout in the backend.
topLevelLayout Restricts whether a Grid Element using this layout may itself be nested inside another container, see Restrictions, Validation & Permissions.
flexformDS A FlexForm data structure for Grid Elements using this layout, see FlexForm.

tx_gridelements.overruleRecords 

Property
tx_gridelements.overruleRecords
Data type
boolean
Description
When a layout ID exists both as a tx_gridelements.setup entry and as a CE backend layout database record, this setting decides which one wins where they overlap. 0 (default) lets the database record's values take precedence; 1 lets the TSconfig values take precedence instead.
Default
0

tx_gridelements.excludeLayoutIds 

Property
tx_gridelements.excludeLayoutIds
Data type
List of strings
Description
A comma-separated list of layout IDs, TSconfig-defined or database records, to exclude from this branch of the page tree. Excluded layouts are filtered out before use, so they are unavailable in this branch even if defined further up the page tree or globally.
Default
N/A

TCEFORM.tt_content.tx_gridelements_backend_layout.PAGE_TSCONFIG_ID 

Property
TCEFORM.tt_content.tx_gridelements_backend_layout.PAGE_TSCONFIG_ID
Data type
Integer
Description
The uid of the page whose storage folder holds a project's CE backend layout database records, so Grid Elements can find them regardless of where in the page tree they are used. Leave unset to resolve layout records from the current page and its own storage folder instead.
Default
N/A

Obsolete option: removeChildrenFromList 

Earlier documentation described TCEFORM.tt_content.tx_gridelements_backend_layout.removeChildrenFromList as a setting that hides a Grid Element's children from the list module, needed at the time to work around problems with the list module's up/down sorting arrows on grid children.

TCEFORM.tt_content.tx_gridelements_backend_layout {
  removeChildrenFromList = 1
}
Copied!

This key is not read by any current code path; setting it has no effect. The underlying problem it addressed is instead handled by the extension configuration option nestingInListModule, which, once enabled, nests grid children under their container in the list module with per-user expand and collapse state rather than hiding them, see List Module Integration. Remove this key from any project's TSconfig; it does nothing.

Where Grid Elements uses TSconfig 

Every option on this page is page TSconfig, resolved for the page a Grid Element or its container lives on, the same mechanism TYPO3 Core uses for page-level Backend Layouts and other editor-facing configuration. Frontend rendering output is configured separately, through TypoScript, see Rendering; the two are never interchangeable, and a grid definition never lives in TypoScript setup.

Data Model 

A Grid Element is a regular tt_content record with CType = gridelements_pi1. What makes it a grid is that it can act as a container for other tt_content records, its children, which are themselves ordinary content elements and may in turn be Grid Elements (see Nesting).

This page describes how that container/child structure is actually stored: which fields exist, which one carries the relation itself, and why colPos is still involved even though it is not that relation. Parent-Child Relations and IRRE builds on this to explain how TYPO3's IRRE (Inline Relational Record Editing) and the DataHandler cooperate to keep these fields consistent while editors work.

Overview 

Grid Element (tt_content, CType = gridelements_pi1)
    |
    | tx_gridelements_children (IRRE, parent side)
    v
Child tt_content
    tx_gridelements_container = <parent uid>   -- the relation
    tx_gridelements_columns   = <cell number>  -- position inside the parent's grid
    colPos                    = -1             -- sentinel, not the relation
    backupColPos                               -- saved colPos while unused
Copied!
Field Stored on Role
tx_gridelements_container child The parent-child relation. Holds the uid of the container record.
tx_gridelements_columns child Which cell of the container's own grid layout the child occupies.
tx_gridelements_children container IRRE parent-side declaration plus a maintained child count. Not an independent relation, see below.
colPos both TYPO3 Core placement and compatibility state, not the Grid Elements relation. Carries sentinel values, see below.
backupColPos both The colPos value to restore once an element's current column becomes available again.

The relation: tx_gridelements_container 

tx_gridelements_container is a select field on the child record that points, by uid, at its parent Grid Element. This is the parent-child relation. Every query that needs to find the children of a given container, including the frontend rendering pipeline in Nesting, filters on this field:

-- GridChildrenProcessor::process(), simplified
SELECT * FROM tt_content WHERE tx_gridelements_container = <container uid>
Copied!

Its TCA (Configuration/TCA/Overrides/tt_content.php) restricts the selectable containers to tt_content records with CType = gridelements_pi1 on the same page and in a matching language, and excludes the record itself and anything the record is already the container of, which prevents the most direct kind of self-reference at the form level (the full cycle guard is described in Parent-Child Relations and IRRE).

The cell: tx_gridelements_columns 

tx_gridelements_columns identifies which cell of the container's own grid layout the child sits in. Its value corresponds to a colPos key defined inside that layout's own grid configuration (rows.<n>.columns.<m>.colPos, see the Grid TS Syntax reference), not to the child's own page-level colPos. A child's position is therefore fully described by the pair (tx_gridelements_container, tx_gridelements_columns): which container, and which cell of that container.

The parent side: tx_gridelements_children 

tx_gridelements_children is declared in TCA as an inline field (foreign_table = tt_content, foreign_field = tx_gridelements_container). This is what makes the container's edit form present and manage its children through TYPO3's IRRE machinery, see Parent-Child Relations and IRRE for how that works.

The field is also a stored integer column, and it is easy to read that as a second, parent-side copy of the relation. It is not. TYPO3's IRRE already determines a container's children purely by querying tx_gridelements_container, exactly as shown above; no code in Grid Elements ever reads which children exist from tx_gridelements_children. What the stored integer holds is a maintained count, kept in sync by AbstractDataHandler::doGridContainerUpdate() every time a child is added to, removed from, or moved out of a container. It exists so the count is available without a separate query, not as a source of truth. If it ever drifts out of sync with the actual number of children, see Data Consistency Tools.

colPos and its sentinel values 

colPos does not establish the Grid Elements parent-child relation. It is TYPO3 Core's own placement field, and Grid Elements keeps writing it for every content element, container children included, because Core and its Page module, list module, and workspace preview all group tt_content by colPos and need a value there regardless of whether an element also happens to be a grid child. The field's underlying database column had to change from an unsigned integer to a signed one to make this possible at all, since Grid Elements relies on two negative sentinel values:

Value Meaning
>= 0 Page-level placement. The element is placed in a column of the page-level Backend Layout and is not a child of another Grid Element.
-1 Grid element column. Set on every child of every container, regardless of which container or which cell, tx_gridelements_columns carries that detail instead.
-2 Non-used elements column. The element currently has no valid column, typically because a layout change removed the column it used to occupy. Surfaced in the backend by giving a page's own backend layout a column configured with colPos = -2.

This column type change must never be reverted by a later upgrade script or manual schema fix. Reverting it back to an unsigned integer means the negative sentinels can no longer be stored. This does not detach a child from its Grid Element: tx_gridelements_container, the actual parent-child relation, is untouched by a colPos schema change. What breaks is TYPO3 Core's own placement handling and backend presentation, which read colPos directly and, unable to see the sentinel value, interpret the affected record incorrectly, typically as if it were an ordinary page-level element rather than a grid child. If this happens, see Data Consistency Tools for the repair tool, which restores the -1 sentinel for any row whose tx_gridelements_container relation is still intact.

backupColPos exists to make the -2 state reversible. When AfterDatabaseOperations::setUnusedElements() detects that a layout change has removed an element's column, it moves the element to colPos = -2 and saves the previous value in backupColPos. When a later layout change makes a matching column available again, the two values are swapped back, restoring the element to its original position without editor intervention.

A related case is a child being detached from its container entirely (tx_gridelements_container reset to 0 while colPos was -1). Rather than defaulting the freed element to column 0, PreProcessFieldArray::checkForRootColumn() walks up the chain of former containers to find the colPos of the outermost page-level ancestor, so the element reappears in the same major page column it structurally originated from.

One container-record field and one separate layout table are worth naming here, since they appear throughout this section: the container's own selected grid layout is stored in tt_content.tx_gridelements_backend_layout as a string identifier. Database-based layout definitions are stored separately in the tx_gridelements_backend_layout table, which contains their title, configuration and icons. Layouts may also be defined through TSconfig, so the value in tt_content.tx_gridelements_backend_layout is a layout identifier rather than an unconditional reference to a row in that table.

Parent-Child Relations and IRRE 

Data Model describes which fields exist and what each one means. This page describes how those fields are kept consistent while editors work: TYPO3's IRRE (Inline Relational Record Editing) infrastructure on the backend-editing side, and the DataHandler hook pipeline that runs on every save, move, copy, and delete.

IRRE in TYPO3, briefly 

IRRE is TYPO3 Core's mechanism for editing a one-to-many relation inline, inside the parent record's own edit form. With foreign_table and foreign_field, the relation is stored directly through a field on the child record rather than through an intermediate MM table. A TCA field of type => inline with foreign_table and foreign_field tells FormEngine: this parent's children are whichever rows in foreign_table have foreign_field pointing back at this record's uid.

Grid Elements uses exactly this configuration, and nothing more exotic. tx_gridelements_children is that inline field, with foreign_table = tt_content and foreign_field = tx_gridelements_container. This matters architecturally, because it means the backend editing widget and the frontend data-fetching path described in Nesting resolve a container's children through the same underlying relation. There is no separate storage format for "children as edited in the backend" versus "children as rendered on the frontend".

Backend editing behavior 

A few TCA settings on tx_gridelements_children shape how the inline widget behaves, worth naming because they explain what an editor sees:

  • appearance.enabledControls disables the widget's own new and dragdrop controls. Children are not created by clicking "new" inside the container's form, they are created through the New Content Element wizard or dragged in from the page module, both of which write tx_gridelements_container directly.
  • overrideChildTca defaults a newly inline-created child's colPos to -1, matching the sentinel described on Data Model.
  • foreign_sortby = sorting means children use the ordinary tt_content sorting field. There is no separate Grid Elements sort order to keep in sync.

The DataHandler pipeline 

Persistent consistency is not left to TCA and IRRE alone. Grid Elements hooks into TYPO3's DataHandler at three points, routed through Classes/Hooks/DataHandler.php and implemented in Classes/DataHandler/:

Hook Responsibility
processDatamap_preProcessFieldArray (PreProcessFieldArray) Runs before a tt_content/pages save is persisted. Keeps colPos, tx_gridelements_container and tx_gridelements_columns consistent with each other, guards against container cycles and dangerous shortcut references before they can be written, and keeps a child's language in sync with its container's language.
processDatamap_afterDatabaseOperations (AfterDatabaseOperations) Runs after the save. Unless automatic unused-column correction has been disabled in the extension configuration, it re-evaluates affected elements against a changed grid or backend layout, moving elements to or from the "unused elements" state described on Data Model, and propagates relation changes to translated copies via checkAndUpdateTranslatedElements().
processCmdmap (ProcessCmdmap) Runs on copy/move commands. Implements "Paste as Reference" by creating a CType = shortcut record instead of duplicating content, and keeps container child counts in sync on copy, move and delete.

A separate pair of hooks, processDatamap_beforeStart and processCmdmap_beforeStart, gate these same operations against a column's allowed/disallowed/maxitems configuration before any of the above runs. That is a restriction-enforcement concern, not a relation-consistency one, and is described on Restrictions, Validation & Permissions.

Keeping counts and translations in sync 

Whenever a save or command changes which container an element belongs to, AbstractDataHandler::doGridContainerUpdate() adjusts the affected containers' tx_gridelements_children counters (see Data Model for what that counter means). It is called from the preprocessing and command-map handling paths as needed when children are moved, copied, attached or detached.

Translations need their own synchronization, because IRRE's foreign_field relation does not automatically re-parent a translated child to a translated container. checkAndUpdateTranslatedElements() propagates a container or child's relation change to its translated counterparts, re-pointing them at the matching container in their own language where one exists, and clearing the relation where it does not, rather than leaving a translated child silently attached to a different language's container.

Cycle protection 

A container becoming its own direct or indirect container would make both backend editing and frontend rendering recurse forever. Classes/Helper/ContainerCycleGuard.php prevents this at two points:

  • Direct or indirect self-reference. getContainerAncestorChain() walks tx_gridelements_container upward from a candidate container to the root, and wouldCreateContainerCycle() rejects an assignment that would place a container inside its own ancestor chain. This runs both on a direct field edit (PreProcessFieldArray::setFieldEntries()) and on a drag-and-drop or cut/paste move (Hooks\DataHandler::processCmdmap_beforeStart()).
  • Shortcut references into an ancestor. A CType = shortcut element referencing one of its own current or prospective ancestor containers would create the same kind of rendering loop indirectly. shortcutReferencesForbiddenContainer() protects individual shortcut edits and moves. When an entire Grid Element subtree is relocated, subtreeReferencesForbiddenContainer() scans its descendants for shortcut references that would become cyclic in the new ancestor chain.

A blocked cmdmap operation is rejected and removed from the command map. During direct datamap editing, the offending relation or reference fields are removed from the incoming field array so the invalid relationship cannot be persisted. In both cases a flash message is shown to the editor.

Nesting 

Nesting is part of Grid Elements' structural model, not a syntax trick layered on top of it. A child of a Grid Element is an ordinary tt_content record (see Data Model), and nothing in the data model restricts what CType that record may have, including gridelements_pi1 itself. A container's child can therefore be a container of its own, with its own children, to whatever depth an editor builds by placing a Grid Element inside another Grid Element's cell through the New Content Element wizard or drag-and-drop. No setting turns nesting "on", and no setting caps how deep it goes: the only structural limit is cycle protection, which prevents a container from becoming its own ancestor. It does not limit legitimate depth.

A layout-level top_level_layout flag can exclude that specific layout from being nested inside another container at all. Separately, per-column allowed/disallowed/maxitems restrictions can limit which layouts or how many elements a specific cell accepts. These are authoring restrictions described in full on Restrictions, Validation & Permissions, not general depth limits on nesting as a mechanism.

It is easy to conflate three distinct things when reading rendering code or templates, so this page keeps them separate.

Structural nesting 

This is the persisted state described above: a chain of tx_gridelements_container relations, unconditional and generic. It exists in the database the moment an editor nests one Grid Element inside another, independent of how the page is ever rendered.

The DataProcessor's recursive option 

GridChildrenProcessor is the recommended way to fetch a container's children for Fluid rendering (see the DataProcessing chapter). Its recursive option controls how many additional levels of children it pre-fetches within a single processing pass:

// GridChildrenProcessor::processChildRecord(), simplified
if ($options['recursive'] && $record['CType'] === 'gridelements_pi1') {
    // re-invoke the same processor on this child, recursive - 1,
    // gathering its children into the same data structure
}
Copied!

With recursive = 0 (the default), a nested container's own children are simply not gathered as part of this processing pass. That is a data-eagerness setting: it changes how many levels of the tree arrive pre-assembled in one processing chain and when deeper containers are processed. It is not a structural limit, and it does not mean nested containers below that point cannot be rendered, see the next section.

Fluid template recursion 

Rendering a container's children in Fluid works through recursion between two partials. Child.html checks whether pre-fetched children data is present for the current record:

<f:if condition="{children}">
    <f:then>
        <f:render partial="Container" arguments="{_all}" optional="true"/>
    </f:then>
    <f:else>
        <f:cObject typoscriptObjectPath="tt_content.{data.CType}" data="{data}" table="tt_content" />
    </f:else>
</f:if>
Copied!

If children was pre-fetched (because recursive was greater than 0, or because this is the container currently being rendered from scratch), the partial renders straight back into Container.html, which walks down through Rows/Columns into Child again.

If it was not pre-fetched, the child is instead rendered through TYPO3's standard tt_content.{CType} dispatch. For a nested gridelements_pi1 record, that re-enters the container's own TypoScript rendering setup from scratch, including its own GridChildrenProcessor call for its own children. The structure still renders correctly and completely; only the fetching strategy differs, one combined pre-fetched pass versus a fresh per-element render. This is what keeps recursive = 0 from ever capping how deep a page actually nests: it changes how deeper levels are fetched, not whether they exist or render.

The legacy TypoScript rendering path (Classes/Plugin/Gridelements.php, deprecated and not recommended for new projects) has its own separate recursive mechanism, based on COA/TEMPLATE TypoScript objects rather than Fluid partials. It is not covered further here; the Rendering section documents the current, recommended DataProcessing and Fluid approach described above.

Because nesting is unconditional at the data-model level, structures like an accordion containing tabs, each containing a multi-column layout, each column containing further Grid Elements, require no special configuration beyond normal backend authoring.

Restrictions, Validation & Permissions 

What an editor is allowed to place into a given cell of a Grid Element, or into a given column of a page, is decided by four separate layers. They are easy to conflate because they operate on the same underlying configuration, but they run at different times, for different reasons, and a bypass at one layer does not imply a bypass at another.

Structural restrictions 

Each cell of a grid layout (page-level Backend Layout or Grid Element layout) can carry allowed, disallowed and maxitems configuration, defined in the layout's TSconfig (see the Grid TS Syntax reference). allowed/disallowed apply by default to CType, list_type and tx_gridelements_backend_layout. Server-side validation also automatically includes any further field mentioned by a column's own allowed or disallowed configuration, so project-specific fields can be protected without changing the restriction format. maxitems caps how many elements a cell accepts.

The column restrictions are used both to filter the normal authoring UI and to validate persisted changes server side. top_level_layout is a separate layout-level nesting restriction whose enforcement path differs, as described below.

This configuration is the shared input to both layers below: what gets offered to an editor, and what actually gets accepted.

Authoring-time filtering (UI layer) 

Several components filter what an editor sees, based on the restrictions above:

  • Grid Element layout options offered in the New Content Element Wizard are filtered through LayoutSetup::getLayoutWizardItems().
  • ModifyNewContentElementWizardItemsListener, a PSR-14 listener on Core's ModifyNewContentElementWizardItemsEvent, filters the wizard's full item list of content element types and plugins, resolving the target column's restrictions either from the page's own backend layout or from the target grid container's layout, depending on where the wizard was opened.
  • The corresponding FormEngine select fields (colPos, CType, list_type) use itemsProcFunc implementations that apply the same filtering to their item lists.

This layer determines what is convenient to pick, not what is permitted. The listener above reads its restriction data (allowed/disallowed) directly from request query parameters, which means any authenticated backend user can construct a request that offers different values than the wizard would. UI filtering alone would not be a security boundary.

Server-side enforcement 

The same restrictions are enforced again, server side, independent of what the UI offered. Classes/Hooks/DataHandler.php implements processDatamap_beforeStart() and processCmdmap_beforeStart(), backed by Classes/Helper/RestrictionGuard.php:

  • processDatamap_beforeStart() checks normal tt_content datamap saves and resolves the effective target column, either the container's own layout and tx_gridelements_columns, or the target page's Backend Layout and colPos. It rejects the save if any restricted field's value is disallowed, or if the column's maxitems would be exceeded.
  • processCmdmap_beforeStart() runs the equivalent check for copy and move commands (drag-and-drop, cut/paste, paste), resolving the same restrictions for the destination the command targets.
  • top_level_layout is handled separately for copy and move operations that would place an existing Grid Element inside another container. It is not part of the generic allowed/disallowed/maxitems field-validation loop.

maxitems enforcement counts existing siblings in the exact target scope (container and column, or page and column, per language) and also accounts for other records being saved in the same request, so several new elements created together cannot collectively exceed the limit even though none of them exists in the database yet.

A rejected save or command is not partially applied: the offending entry is removed from the datamap or cmdmap before TYPO3 processes it, and a flash message explains why. This makes allowed/disallowed/maxitems a real constraint on what ends up stored, not merely a convenience for what the wizard suggests. This document makes no claim about enforcement behavior in other maintained branches.

Permissions and auth mode 

The layers above decide what is allowed in a particular cell. A separate, TYPO3 Core mechanism decides whether a given backend user may use a particular CType at all: explicit-allow/deny auth mode, checked via BackendUserAuthentication::checkAuthMode() and configured per backend user or group, the same mechanism Core uses for any other restricted field value.

Grid Elements gates two of its own features behind this check rather than inventing a separate permission system:

  • ModifyNewContentElementWizardItemsListener only runs at all if the current user's permissions allow the gridelements_pi1 CType. A user without that permission does not see the Grid Elements branch of the wizard.
  • The "Paste as Reference" context menu action only renders if the user's permissions allow the shortcut CType (ItemProvider::canRender()), since creating a reference means creating a shortcut record.

Summary 

Layer Decides Enforced by
Structural restrictions What a layout's top_level_layout setting and a column's allowed/disallowed/maxitems configuration say Layout TSconfig, typically authored through the Grid Wizard, the shared input to both layers below
Authoring-time filtering What is offered to an editor Wizard item listener, itemsProcFunc implementations
Server-side enforcement What is actually accepted and stored DataHandler beforeStart hooks, RestrictionGuard
Permissions / auth mode Whether a user may use a given CType at all TYPO3 Core checkAuthMode()

Data Consistency Tools 

Under normal operation, the DataHandler hooks described on Parent-Child Relations and IRRE keep colPos and the tx_gridelements_children counter (see Data Model) consistent automatically as editors work. The two tools on this page exist for exceptional cases where those values nevertheless become inconsistent, for example because an external process writes to tt_content outside a normal Grid Elements-aware save or because an edge case bypasses the expected maintenance behavior. They are corruption-repair tools for exceptional states, not a routine or scheduled maintenance step.

Grid Elements Col Pos Fixer 

Fixes grid children whose colPos is no longer -1. Per its own docblock, this is aimed at the specific case of a TYPO3 Core major-version upgrade: if Grid Elements' own signed SQL schema definition for colPos is not part of the active set a schema migration compares against during that upgrade, TYPO3 Core's unsigned definition can be applied to the column instead, which can affect the negative sentinel values stored there, see Data Compatibility Across TYPO3 Versions for the schema mechanism itself. This tool restores the -1 sentinel for any row whose tx_gridelements_container relation is still intact.

The fixer does not try to detect which rows are broken. It unconditionally reasserts the sentinel for every row that currently has a container:

UPDATE tt_content SET colPos = -1 WHERE tx_gridelements_container > 0
Copied!

Grid Elements Number Of Children Fixer 

Fixes the maintained tx_gridelements_children counter described on Data Model. Per its own docblock, this addresses drift caused by buggy cut/copy/paste or drag-and-drop behavior in edge cases. For every gridelements_pi1 container, it counts the container's actual children directly (tx_gridelements_container = <container uid>) and writes that count back, replacing whatever value the counter currently holds.

Running the tools 

Both are available as TYPO3 CLI commands and as classic TYPO3 Scheduler task types, so either a CLI or a backend-scheduled workflow can trigger the repair.

Tool Console command Scheduler task class
Col Pos Fixer vendor/bin/typo3 gridelements-col-pos-fixer GridElementsTeam\Gridelements\Task\GridelementsColPosFixer
Number Of Children Fixer vendor/bin/typo3 gridelements-number-of-children-fixer GridElementsTeam\Gridelements\Task\GridelementsNumberOfChildrenFixer

Neither tool needs to run on a schedule for Grid Elements to function correctly. Reach for them after a known external interference with tt_content, such as the upgrade scenario described above, or when the tx_gridelements_children count or a grid child's colPos sentinel is known to be inconsistent and normal backend editing does not correct it.

Rendering 

Everything a template needs to render a Grid Element's children, the parent-child relation, the cell each child occupies, the grid's row and column layout, already exists once an editor has built the structure in the backend, see Data Model and Nesting. Rendering does not create or define that structure. It reads it.

This section covers how a Grid Element's stored structure reaches the frontend output:

Rendering Architecture 

A Grid Element's frontend output ultimately needs three things that already exist by the time rendering starts: which children belong to the container (Data Model), which cell of the container's grid each child occupies, and the container's own row/column layout. None of that is created by rendering. It is read by rendering, from structure that backend authoring already established, see Data Model and Nesting for how that structure is stored and why it nests without limit. This page is about how that stored structure reaches Fluid or TypoScript output, not about the structure itself.

Two rendering paths currently coexist in Grid Elements. Both start from that same stored structure and both produce working output, but they get there through unrelated code. The recommended path looks like this:

stored Grid Elements structure
        |
        v
relation and layout information (LayoutSetup)
        |
        v
DataProcessing (GridChildrenProcessor)
        |
        v
structured data for Fluid
        |
        v
frontend output
Copied!

The deprecated legacy path starts from the same stored structure and the same LayoutSetup layout information, but does not use GridChildrenProcessor to hand the structured Grid Elements data model to Fluid. It renders directly, through nested TypoScript COA/TEMPLATE objects driven by a USER cObject, and it can use FLUIDTEMPLATE for that rendering and expose its legacy virtual fields to Fluid-based templates the same way it exposes them to TypoScript-based ones. Both paths are described in detail below.

The legacy path: TypoScript and userFunc 

Classes/Plugin/Gridelements.php is the original rendering implementation, predating DataProcessing as a TYPO3 Core feature. It is a USER cObject: its main() method queries the same tx_gridelements_container relation directly, resolves the container's grid layout through LayoutSetup, and renders the result itself through nested COA/TEMPLATE TypoScript objects rather than handing structured data to Fluid.

The class and its public methods are marked @deprecated in source. Grid Elements treats this path as deprecated, not recommended for new projects. It remains available and functional for existing projects that already render through it, and this documentation keeps its configuration and reference material available for that reason. New projects should use DataProcessing and Fluid instead.

The role of LayoutSetup 

Classes/Backend/LayoutSetup.php resolves a Grid Element layout's own row/column configuration, its allowed/disallowed/ maxitems per cell, into a normalized array through checkAvailableColumns(). Both rendering paths depend on this: the legacy plugin calls LayoutSetup::init() and getLayoutColumns() directly in main(), and GridChildrenProcessor calls the same init()/getLayoutSetup() methods to resolve the container's layout for its respectColumns/ respectRows grouping. LayoutSetup is the shared parsing layer underneath both rendering paths, even though the paths do nothing else in common.

This is a distinct code path from GridElementsHelper::getSelectedBackendLayout(), which parses the same kind of allowed/disallowed/maxitems configuration but from a page's own Backend Layout rather than a Grid Element's layout, and feeds authoring-time restriction enforcement rather than rendering, see Restrictions, Validation & Permissions. The two are independently implemented, not one calling the other, though both normalize through the same shared helper, GridElementsHelper::mergeAllowedDisallowedSettings(). Rendering only ever depends on LayoutSetup; a rendering template has no reason to call getSelectedBackendLayout().

What rendering is not responsible for 

Neither rendering path creates the parent-child relation, decides nesting depth, or enforces allowed/disallowed/maxitems. Those are backend authoring and DataHandler concerns, settled before a page is ever rendered, see Parent-Child Relations and IRRE, Nesting and Restrictions, Validation & Permissions. A rendering configuration change, including switching between the two paths described here, changes how a structure is displayed. It never changes what that structure is.

DataProcessing 

DataProcessing is the recommended way to render a Grid Element's children. See Rendering Architecture for how it fits alongside the deprecated legacy TypoScript path; this page is about using it.

GridElementsTeam\Gridelements\DataProcessing\GridChildrenProcessor is a standard TYPO3 DataProcessorInterface implementation. Add it to a content object's dataProcessing array like any other processor, and it fetches the current container's children, the tt_content records related to it through tx_gridelements_container (see Data Model), resolves their FlexForm and backend layout data as configured, and hands the result to Fluid as a plain variable. What that variable looks like, a flat list or a rows/columns matrix grouped by the container's own grid layout, is controlled by its options, see Reference. How to configure and use those options, and the Fluid partials that consume the result, is covered in How To.

Grid Elements ships a ready-to-use static TypoScript template, Gridelements w/DataProcessing, that wires up GridChildrenProcessor on a FLUIDTEMPLATE for tt_content.gridelements_pi1:

tt_content.gridelements_pi1 =< lib.contentElement
tt_content.gridelements_pi1 {
    templateName = GridElement
    templateName.override.field = tx_gridelements_backend_layout
    templateRootPaths {
        1 = EXT:gridelements/Resources/Private/Templates/
    }

    partialRootPaths {
        1 = EXT:gridelements/Resources/Private/Partials/
    }

    dataProcessing {
        10 = GridElementsTeam\Gridelements\DataProcessing\GridChildrenProcessor
        10 {
            default {
                as = children
                # Default options of the grid children processor
                # Change them according to the needs of your layout
                # See the Reference page for the full option list
                # options {
                #    sortingDirection = ASC
                #    sortingField = sorting
                #    recursive = 0
                #    resolveFlexFormData = 1
                #    resolveChildFlexFormData = 1
                #    resolveBackendLayout = 1
                #    respectColumns = 1
                #    respectRows = 1
                #}
            }
        }
    }
}

lib.tt_content.shortcut.pages = COA
lib.tt_content.shortcut.pages {
    5 = LOAD_REGISTER
    5 {
        tt_content_shortcut_recursive.field = recursive
    }

    10 = USER
    10 {
        userFunc = GridElementsTeam\Gridelements\Plugin\Gridelements->user_getTreeList
    }

    20 = CONTENT
    20 {
        table = tt_content
        select {
            pidInList.data = register:pidInList
            selectFields.dataWrap = *,FIND_IN_SET(pid,{register:pidInList}) AS gridelements_shortcut_page_order_by
            where = colPos >= 0
            languageField = sys_language_uid
            orderBy = gridelements_shortcut_page_order_by,colPos,sorting
        }
    }

    30 = RESTORE_REGISTER
}

tt_content.shortcut.variables.shortcuts {
    tables := addToList(pages)
    conf.pages < lib.tt_content.shortcut.pages
}
Copied!

This is a normal FLUIDTEMPLATE setup with template, layout and partial root paths; GridChildrenProcessor fetches the children, and anything else added to this setup follows regular TypoScript syntax, including other data processors run alongside it. The commented block shows the available options with their default values, see Reference for what each one does. Use the f:debug view helper in a template to inspect what a given configuration actually produces:

<f:debug>{_all}</f:debug>
Copied!

Every option and internal key is also passed through stdWrap, so values can be assigned dynamically rather than only as TypoScript constants.

For how this processor's option model compares to EXT:container's own ContainerProcessor, including which option configuration approximates its behavior, see Grid Elements and EXT:container.

How To 

This guide outlines the configuration of the GridChildrenProcessor, its options, and examples of corresponding templates and partials for rendering grid-based layouts in TYPO3.

Example Configuration 

Below is an example GridChildrenProcessor configuration:

lib.gridelements {
    templateName = GridElement
    templateName.override.field = tx_gridelements_backend_layout
    templateRootPaths {
        1 = EXT:gridelements/Resources/Private/Templates/
    }

    partialRootPaths {
        1 = EXT:gridelements/Resources/Private/Partials/
    }

    dataProcessing {
        10 = GridElementsTeam\Gridelements\DataProcessing\GridChildrenProcessor
        10 {
            default {
                as = children
                options {
                    sortingDirection = asc
                    sortingField = sorting
                    recursive = 0
                    resolveFlexFormData = 1
                    resolveChildFlexFormData = 1
                    resolveBackendLayout = 1
                    respectColumns = 1
                    respectRows = 1
                }
            }
        }
    }
}
Copied!

---

Templates and Partials 

The templates and partials used with GridChildrenProcessor extend TYPO3's grid-based rendering functionality. Samples are provided below for each template and partial used in the configuration. Those samples are provided by Grid Elements out of the box, but you can of course still create your own templates with fewer conditions, if your layouts are strictly bound to a certain configuration. Just add those settings to the TypoScript template based on your Grid Elements backend layouts and configure them to your needs.

10 = GridElementsTeam\Gridelements\DataProcessing\GridChildrenProcessor
10 {
    default {
        [...]
    }
    myLayout1 {  # Custom key for this layout
        as = children
        options {
            sortingField = title  # Sort child elements alphabetically by their title
            sortingDirection = desc  # Change sorting direction to descending
            recursive = 0  # Do not pre-fetch a nested container's own children in this pass
            resolveFlexFormData = 0  # Keep original FlexForm XML without conversion
            resolveBackendLayout = 0  # Disable resolution of backend layouts
            respectColumns = 0  # Disable column grouping
            respectRows = 0  # Also disable row grouping; respectRows defaults to 1 and row handling requires column handling, so both must be off for a genuinely flat list
        }
    }
    myLayout2 {  # Custom key for this layout
        as = somethingElse  # use a different key to deal with your child elements
        options {
            recursive = 1  # Pre-fetch one additional level of nested container children in this pass
            respectColumns = 1  # Group children by their column value
            respectRows = 0  # Skip grouping by rows, e.g. with a single row configuration
        }
    }
}
Copied!

recursive only changes how many levels of children this processing pass pre-fetches in one go. It is not a structural depth limit; nesting itself is unconditional and unaffected by this option, see Nesting for the full distinction between structural nesting, this option, and the Fluid template recursion described below.

Template: GridElement.html 

This is the main entry template for rendering the grid layout.

<html xmlns:f="http://typo3.org/ns/TYPO3/CMS/Fluid/ViewHelpers" data-namespace-typo3-fluid="true">
    <f:layout name="Default"/>
    <f:section name="Main">
        <f:render partial="Container" arguments="{_all}"/>
    </f:section>
</html>
Copied!

Partial: Container.html 

The Container partial is the central rendering controller. It uses the switches respectColumns and respectRows to dynamically determine whether to render content hierarchically by rows, columns, or as a flat list of child elements.

If `respectColumns` is enabled: - Child elements are grouped into columns according to their tx_gridelements_columns values. - Each column group is rendered by the Columns partial.

If `respectRows` is also enabled: - Child elements are first grouped into rows based on the layout configuration. - Within each row, child elements are further grouped into their respective columns, using the Rows and Columns partials.

If neither switch is enabled: - All child elements are rendered as a flat list using the Child partial.

<html xmlns:f="http://typo3.org/ns/TYPO3/CMS/Fluid/ViewHelpers" data-namespace-typo3-fluid="true">
    <div class="grid-container grid-container-{data.tx_gridelements_backend_layout}">
        <f:if condition="{options.respectColumns}">
            <f:then>
                <f:if condition="{options.respectRows}">
                    <f:then>
                        <f:render partial="Rows" arguments="{data: data, rows: children, options: options, settings: settings}"/>
                    </f:then>
                    <f:else>
                        <f:render partial="Columns" arguments="{data: data, columns: children, options: options, settings: settings}"/>
                    </f:else>
                </f:if>
            </f:then>
            <f:else>
                <f:if condition="{children}">
                    <f:for each="{children}" as="child">
                        <f:render partial="Child" arguments="{data: child.data, children: child.children, options: options, settings: settings}"/>
                    </f:for>
                </f:if>
            </f:else>
        </f:if>
    </div>
</html>
Copied!

Partial: Rows.html 

This partial organizes child elements into rows and delegates further rendering to the Columns partial if respectColumns is enabled.

<html xmlns:f="http://typo3.org/ns/TYPO3/CMS/Fluid/ViewHelpers" data-namespace-typo3-fluid="true">
    <f:if condition="{rows}">
        <f:for each="{rows}" as="columns" key="rowNumber">
            <div id="c{data.uid}-{rowNumber}" class="grid-row grid-row-{rowNumber}">
                <f:render partial="Columns" arguments="{data: data, columns: columns, options: options, settings: settings}"/>
            </div>
        </f:for>
    </f:if>
</html>
Copied!

Partial: Columns.html 

Handles rendering of columns by grouping child elements into their respective tx_gridelements_columns values. Child elements within each column are delegated to the Child partial.

<html xmlns:f="http://typo3.org/ns/TYPO3/CMS/Fluid/ViewHelpers" data-namespace-typo3-fluid="true">
    <f:if condition="{columns}">
        <f:for each="{columns}" as="column" key="columnNumber">
            <div id="c{data.uid}-{columnNumber}" class="grid-column grid-column-{columnNumber}">
                <f:for each="{column}" as="child">
                    <f:render partial="Child" arguments="{data: child.data, children: child.children, options: options, settings: settings}"/>
                </f:for>
            </div>
        </f:for>
    </f:if>
</html>
Copied!

Partial: Child.html 

Renders individual child elements. If the child element itself is a container, this partial recursively calls the Container partial to render its children.

<html xmlns:f="http://typo3.org/ns/TYPO3/CMS/Fluid/ViewHelpers" data-namespace-typo3-fluid="true">
    <f:if condition="{children}">
        <f:then>
            <a id="c{data.uid}"></a>
            <f:if condition="{data._LOCALIZED_UID}">
                <a id="c{data._LOCALIZED_UID}"></a>
            </f:if>
            <f:render partial="Container" arguments="{_all}" optional="true"/>
        </f:then>
        <f:else>
            <f:if condition="{data.CType}">
                <f:cObject typoscriptObjectPath="tt_content.{data.CType}" data="{data}" table="tt_content" />
            </f:if>
        </f:else>
    </f:if>
</html>
Copied!

This is the Fluid template recursion described in Nesting: when pre-fetched children data is present, Child.html renders straight back into the Container partial; otherwise the child is rendered through TYPO3's standard tt_content.{CType} dispatch, which re-enters this same processing chain from scratch if that child is itself a Grid Element. Either way, the structure renders completely regardless of how deep it nests.

---

Advantages 

  • Flexible Layout Management: Powerful control over rendering child elements as rows, columns, or lists. Adjust processing dynamically with respectRows and respectColumns.
  • Dynamic Nesting: Nested grid structures render at any depth without extra configuration, see Nesting. The recursive option only controls how many of those levels this processing pass pre-fetches in one go.
  • Adaptable Templates: Templates and partials reflect layout variations, allowing developers to customize rendering for unique use cases.
  • Streamlined Processing: Integrates with TYPO3's dataProcessing to combine FlexForm data, sorting, and grouping in a single pipeline.

Reference 

See Rendering Architecture for how GridChildrenProcessor fits into rendering overall, and DataProcessing for how to configure it. This page lists its TypoScript options and their defaults.

TypoScript 

setup 

Property
dataProcessing
Data type
array of class references by full namespace
Description
Add one or multiple processors to manipulate the $data variable of the currently rendered content object, like tt_content or page. The sub-property options can be used to pass parameters to the processor class.
Default
10 = GridElementsTeam\Gridelements\DataProcessing\GridChildrenProcessor

dataProcessing.123.default 

Property
default
Data type
Internal
Description
The default setup used by any Grid Element layout that has not got its own setup available. Layouts are assigned by their identifier. Just provide individual blocks myIdentifier{...} for each layout.
Default
default

dataProcessing.123.default.as 

Property
as
Data type
Internal / stdWrap
Description
This will be the name of the variable filled with the output generated by the GridChildrenProcessor. You can access it via {children} from within your Fluid template.
Default
children

dataProcessing.123.default.options 

Property
options
Data type
Internal
Description
Can contain any of the following options to determine the behaviour of the GridChildrenProcessor.
Default
N/A

dataProcessing.123.default.options.sortingDirection 

Property
sortingDirection
Data type
String / stdWrap
Description
Determines the sorting direction of the database query for children. Only desc is treated specially; any other value, including asc, results in ascending sorting.
Default
asc

dataProcessing.123.default.options.sortingField 

Property
sortingField
Data type
String / stdWrap
Description
Determines the sorting field of the database query for children. Can be any field name but you have to make sure that the field exists yourself, otherwise the query might fail.
Default
sorting

dataProcessing.123.default.options.recursive 

Property
recursive
Data type
Integer / stdWrap
Description
Determines how many additional levels of a nested container's own children are pre-fetched within this single processing pass. This is a data-eagerness setting, not a structural depth limit; nesting itself is unconditional regardless of this value, see Nesting. The whole pre-fetched tree of children will be handed over to the variable as a data array. Be aware of the fact that this ignores different setups of child elements
Default
0

dataProcessing.123.default.options.resolveFlexFormData 

Property
resolveFlexFormData
Data type
Boolean / stdWrap
Description
Determines if FlexForms of containers should be resolved and assigned to virtual fields named flexform\_my\_fieldname. Setting this option to 0 will disable resolving of child FlexForms too, unless you explicitly allow them using resolveChildFlexFormData
Default
1

dataProcessing.123.default.options.resolveChildFlexFormData 

Property
resolveChildFlexFormData
Data type
Boolean / stdWrap
Description
Determines if FlexForms of children should be resolved and assigned to virtual fields named flexform\_my\_fieldname. The default will be overridden by setting resolveFlexFormData to 0
Default
1

dataProcessing.123.default.options.resolveBackendLayout 

Property
resolveBackendLayout
Data type
Boolean / stdWrap
Description
Determines if CE-BackendLayouts should be resolved. This is applied to the current Grid Element itself, not only to its children: its resolved layout information is required for processing the Grid structure, including its rows and columns. Child Grid Elements may then also have their Backend Layout information resolved, assigned to virtual fields named tx\_gridelements\_backend\_layout\_resolved. Use this information e.g. to generate CSS classes based on layout data.
Default
1

dataProcessing.123.default.options.respectColumns 

Property
respectColumns
Data type
Boolean / stdWrap
Description
Determines if layout columns should be recognized and assigned while rendering children. When enabled, children itself becomes a matrix indexed by the Grid Elements structural column identifiers, based on tx\_gridelements\_columns, instead of a flat list.
Default
1

dataProcessing.123.default.options.respectRows 

Property
respectRows
Data type
Boolean / stdWrap
Description
Determines if layout rows should be recognized and assigned while rendering columns. When enabled, children itself is indexed by row instead of by column, and each row directly contains the column-indexed child arrays described under respectColumns. Sets respectColumns internally if not set.
Default
1

Legacy TypoScript Rendering 

This is the default TypoScript setting provided while including the legacy Gridelements setup in your TS template editor:

lib.gridelements.defaultGridSetup {
    // stdWrap functions being applied to each element
    columns {
        default {
            renderObj = COA
            renderObj {
                # You can use registers to e.g. provide different image settings for each column
                # 10 = LOAD_REGISTER
                20 =< tt_content

                # And you can reset the register later on
                # 30 = RESTORE_REGISTER
            }
        }

        #2 < .default
        #2 {
        #}
    }

    # if you want to provide your own templating, just insert a cObject here
    # this will prevent the collected content from being rendered directly
    # e.g. cObject = TEMPLATE or cObject = FLUIDTEMPLATE will be available from the core
    # the content will be available via field names like
    # tx_gridelements_view_columns (an array containing each column)
    # or tx_gridelements_view_children (an array containing each child)
    # tx_gridelements_view_column_123 (123 is the number of the column)
    # or tx_gridelements_view_child_123 (123 is the UID of the child)
}

tt_content.gridelements_pi1 = COA
tt_content.gridelements_pi1 {
    #10 =< lib.stdheader
    20 = COA
    20 {
        10 = USER
        10 {
            userFunc = GridElementsTeam\Gridelements\Plugin\Gridelements->main
            setup {
                default < lib.gridelements.defaultGridSetup
            }
        }
    }
}

tt_content.gridelements_view < tt_content.gridelements_pi1

lib.tt_content.shortcut.pages = COA
lib.tt_content.shortcut.pages {
    5 = LOAD_REGISTER
    5 {
        tt_content_shortcut_recursive.field = recursive
    }

    10 = USER
    10 {
        userFunc = GridElementsTeam\Gridelements\Plugin\Gridelements->user_getTreeList
    }

    20 = CONTENT
    20 {
        table = tt_content
        select {
            pidInList.data = register:pidInList
            selectFields.dataWrap = *,FIND_IN_SET(pid,{register:pidInList}) AS gridelements_shortcut_page_order_by
            where = colPos >= 0
            languageField = sys_language_uid
            orderBy = gridelements_shortcut_page_order_by,colPos,sorting
        }
    }

    30 = RESTORE_REGISTER
}

tt_content.shortcut.variables.shortcuts {
    tables := addToList(pages)
    conf.pages < lib.tt_content.shortcut.pages
}
Copied!

As you can see, it is just based on the usual TypoScript and uses some internal keys, like columns, default and renderObj to define the setup for the columns. Anything else you want to use will be based on the official TypoScript syntax, so no extension-specific parameters are involved.

As described in the commented part, you will find some additional virtual fields in your data, containing data gathered during the rendering process. These come in handy, when you want to use a TEMPLATE or FLUIDTEMPLATE element to produce your output. Just use the debug view helper in your template to get an overview of the available fields.

<f:debug>{_all}</f:debug>
Copied!

Any of the internal keys and the default settings will of course be passed to the stdWrap method, so you can assign almost anything to any part of your setup.

The two setups for the shortcut cObject are used to render the references properly, so changing them should be done only with a clear understanding of their effect.

See Reference for the full list of internal keys and virtual fields this rendering path provides.

Reference 

Part of the legacy TypoScript rendering path, deprecated and not recommended for new projects, see Rendering Architecture. Retained for existing projects; new projects should use the Reference instead.

TypoScript 

setup 

Property
setup
Data type
Internal /stdWrap
Description
The setup handed over to the userFunc that is responsible for the rendering process
Default
N/A

setup.default 

Property
setup.default
Data type
Internal / stdWrap
Description

The default setup used by any Grid Element layout that has not got its own setup available. Layouts are assigned by their layout identifier: for a database-backed CE backend layout record this is an explicitly configured alias where one exists, or the record's UID otherwise; a TSconfig-defined layout has its own identifier, see TSconfig.

It usually contains the columns parameter and might use additional stdWrap methods. If you provide a cObject as stdWrap method, this will override the default output and give you the option to make use of the special virtual fields containing prerendered content.

Default
N/A

setup.123 

Property
setup.123
Data type
Internal / stdWrap
Description
This setup will be used for the Grid Element layout with the layout identifier 123, an alias or, absent one, a UID for a database-backed layout, or a TSconfig-defined layout's own identifier
Default
N/A

columns 

Property
columns
Data type
Internal / stdWrap
Description
This contains the setup for the default column and any other column that might differ from the default. Columns are assigned by their column value.
Default
N/A

columns.default 

Property
columns.default
Data type
Internal / stdWrap
Description
This will be the setup used for any column that has not got its own setup available
Default
N/A

columns.123 

Property
columns.123
Data type
Internal / stdWrap
Description
This will be used for the column with the column value 123
Default
N/A

renderObj 

Property
renderObj
Data type
cObject / stdWrap
Description
Can be any kind of TypoScript content object, like TEXT or TEMPLATE
Default
COA

flexform_fieldname 

Property
flexform_fieldname
Data type
Flexform field
Description
Get the value of a field defined in the flexform of the grid layout of the current cObj
Default
N/A

parentgrid_fieldname 

Property
parentgrid_fieldname
Data type
Value of the parent container
Description
Get the value of a field of the parent container of the current cObj
Default
N/A

tx_gridelements_view_children 

Property
tx_gridelements_view_children
Data type
Array
Description
An array containing all the children as raw data
Default
N/A

tx_gridelements_view_columns 

Property
tx_gridelements_view_columns
Data type
Array
Description
An array containing the prerendered columns
Default
N/A

tx_gridelements_view_column_123 

Property
tx_gridelements_view_column_123
Data type
HTML
Description
The prerendered content of column 123
Default
N/A

tx_gridelements_view_child_123 

Property
tx_gridelements_view_child_123
Data type
HTML
Description
The prerendered child with the uid 123
Default
N/A

Developer Reference 

The preceding sections describe Grid Elements from an editor's or an integrator's point of view: how a grid structure is defined (Grid Definitions), how its data is stored (Data Model), how editors work with it (Editor Guide), and how it reaches frontend output (Rendering). This section is for a different audience: developers extending Grid Elements itself, writing another extension that needs to cooperate with it, or maintaining a project that XCLASSes or overrides part of TYPO3's backend and needs to know where Grid Elements already touches that same code.

Grid Elements is implemented almost entirely through TYPO3 Core's PSR-14 events and a small number of legacy $GLOBALS hooks that Core has not replaced with events for the relevant operations yet. XCLASSing is exceptional and limited to a single, explicitly opt-in case. Extension Points catalogs all of these by their actual current class and event names, and states plainly where an older hook has been superseded rather than presenting it as current.

Where to go next 

Extension Points PSR-14 events Grid Elements listens to and dispatches, the remaining legacy hooks, and its one XCLASS.
List Module Integration How Grid Elements optionally integrates with TYPO3's Web > List module, and how that integration relates to (and stays distinct from) the Grid Elements parent-child relation and colPos.

Extension Points 

Grid Elements integrates with TYPO3's backend and DataHandler almost entirely through PSR-14 events, all registered in Configuration/Services.yaml. A handful of legacy $GLOBALS hooks remain in use where Core does not yet offer an event for the relevant operation. XCLASSing is exceptional: exactly one core class is XCLASSed, and only when a project explicitly opts in. This page lists the actual current mechanisms by class and event name, and calls out the two places where an older registration has become dead code rather than describing it as if it still ran.

PSR-14 events Grid Elements listens to 

Core event Listener Purpose
AfterTcaCompilationEvent EventListener\ExtTablesInclusionPostProcessing Registers Grid Elements' Page Module preview renderers (GridelementsPreviewRenderer, ShortcutPreviewRenderer) into the compiled TCA.
BeforeFlexFormDataStructureIdentifierInitializedEvent EventListener\BeforeFlexFormDataStructureIdentifierInitializedListener Resolves the FlexForm data-structure identifier for gridelements_pi1 containers.
BeforeFlexFormDataStructureParsedEvent EventListener\BeforeFlexFormDataStructureParsedListener Supplies the default Grid Elements FlexForm data structure (or a project-defined flexformDS) for that identifier.
TYPO3\CMS\Backend\View\Event\ModifyDatabaseQueryForRecordListingEvent EventListener\ModifyDatabaseQueryForRecordListingListener Excludes grid children (colPos = -1) from the List module's base query, see List Module Integration.
TYPO3\CMS\Backend\Controller\Event\ModifyNewContentElementWizardItemsEvent EventListener\ModifyNewContentElementWizardItemsListener Filters the New Content Element Wizard's item list against the target cell's allowed/disallowed restrictions, see Restrictions, Validation & Permissions.
TYPO3\CMS\Backend\View\Event\IsContentUsedOnPageLayoutEvent EventListener\IsContentUsedOnPageLayoutListener Marks a grid child (colPos = -1 with a set tx_gridelements_container) as used. This is the current, functioning replacement for the record_is_used hook, see below.
TYPO3\CMS\Backend\Controller\Event\AfterBackendPageRenderEvent EventListener\AfterBackendPageRendererEventListener Injects Grid Elements' JavaScript language labels into the backend page.
TYPO3\CMS\Backend\Controller\Event\ModifyPageLayoutContentEvent EventListener\ModifyPageLayoutContentEventListener Exposes clipboard paste settings and loads the Drag-In Wizard JavaScript module in the Page Module, see Drag-In Wizard.

Grid Elements' own event 

GridElementsTeam\Gridelements\Event\ModifyRecordListElementDataEvent is dispatched by Grid Elements itself, from the List module XCLASS described below, once per row, and implements Psr\EventDispatcher\StoppableEventInterface. Its own listener, EventListener\ModifyRecordListElementDataListener, attaches the container/child markers (_CONTAINER_COLUMNS_, _EXPANDABLE_, _EXPAND_ID_, _EXPAND_TABLE_, _LEVEL_, _CHILDREN_) the List module rendering needs, but it is a regular PSR-14 event on the event bus like any other: another extension can listen to it too, for example to add its own markers to the same row data.

TYPO3 hooks still in use 

These are legacy $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS'] hook registrations, kept because Core has not replaced them with PSR-14 events for the operations they cover.

Hook point Class Purpose
t3lib/class.t3lib_tcemain.php processDatamapClass Hooks\DataHandler Field-array preprocessing and post-save consistency work, see Parent-Child Relations and IRRE, plus restriction enforcement, see Restrictions, Validation & Permissions.
t3lib/class.t3lib_tcemain.php processCmdmapClass Hooks\DataHandler Copy/move command handling ("Paste as Reference", child-count maintenance, cycle protection) and restriction enforcement for commands, see Parent-Child Relations and IRRE and Restrictions, Validation & Permissions.
t3lib/class.t3lib_tcemain.php moveRecordClass Hooks\DataHandler Registered against the same class, but Hooks\DataHandler does not currently implement a moveRecord() method, so this particular registration has no effect. Move-time consistency is instead handled through processCmdmap() and processCmdmap_beforeStart(), which do run on a drag-and-drop or cut/paste move.

Two further hook registrations exist in ext_tables.php but no longer run, because the TYPO3 Core hook points they target have been removed in the versions this branch supports (^12.4 || ^13.4.7):

  • ``cms/layout/class.tx_cms_layout.php`` ``record_is_used`` (Hooks\PageLayoutView) is only registered for TYPO3 < 13, but the record_is_used hook point it targets was already removed from Core in TYPO3 12 (Breaking-98375), and the core class its method type-hints was itself removed in TYPO3 13. The class and its registration remain in the codebase as dead code across the whole supported version range. IsContentUsedOnPageLayoutListener above is the current, functioning mechanism; do not treat Hooks\PageLayoutView as active.
  • ``typo3/class.db_list_extra.inc`` ``actions`` (Hooks\DatabaseRecordList, registered only when nestingInListModule is enabled) targets a hook point Core no longer dispatches either. Only one method on that class, contentCollapseIcon(), remains functional, and it is reached by a direct call from the XCLASS below, not through this hook point, see List Module Integration.

XCLASS (exceptional) 

Grid Elements XCLASSes exactly one Core class, TYPO3\CMS\Backend\RecordList\DatabaseRecordList, and only when a project has explicitly enabled the nestingInListModule extension configuration option. Two implementations are registered depending on the running Core version, since the parent class's own API differs between them:

TYPO3 Class Notes
13 Xclass\DatabaseRecordList Marked @internal in its own docblock: it extends a Core class that is explicitly not part of Core's public API, and can change in a minor release. Do not XCLASS it further; extend Grid Elements' own event (above) instead where possible.
12 Xclass\DatabaseRecordList12 Same role, adapted to the TYPO3 12 version of the parent class.

See List Module Integration for what this XCLASS actually renders and why an event alone was not enough for it.

Other Core registration points 

A few further integrations use Core's own configuration-array registries rather than a hook or an event. They are listed here for completeness, since each is a place a project could otherwise assume Grid Elements uses a hook where it does not:

  • FormEngine node registry ($GLOBALS['TYPO3_CONF_VARS']['SYS']['formEngine']['nodeRegistry']) registers Wizard\GridelementsBackendLayoutWizardElement (TYPO3 13) or Wizard\GridelementsBackendLayoutWizardElement12 (TYPO3 12) as the belayoutwizard FormEngine node, the Grid Wizard's backend rendering, see Grid Wizard.
  • Context menu item providers ($GLOBALS['TYPO3_CONF_VARS']['BE']['ContextMenu']['ItemProviders']) registers ContextMenu\ItemProvider for the "Paste as Reference" action, see References.
  • Scheduler task types and console commands register the data consistency tools described in full at Data Consistency Tools.

For how these mechanisms compare to EXT:container's own hooks, events and class-replacement overrides, and the technically meaningful sense in which "Core-native" does and does not apply to either extension, see Grid Elements and EXT:container.

List Module Integration 

By default, TYPO3's Web > List module lists tt_content records the same way it lists any other table: a flat list per colPos, with no awareness that some of those rows are Grid Element children. Grid Elements can instead nest a container's children under it in the List module, with per-user expand and collapse state, but this is off by default and must be enabled explicitly per project through the nestingInListModule boolean extension configuration option (disabled: 0).

What identifies a row's place in the list 

As throughout Grid Elements, it is not colPos that the nested list is built from, see Data Model. Two separate fields do that work, and they stay distinct here exactly as they do everywhere else: tx_gridelements_container, stored on the child, is the parent Grid Element relation and is what determines which container a row is nested under; tx_gridelements_columns, also stored on the child, is the structural cell/column assignment within that container and is what groups a container's children by cell once they are nested under it. colPos still plays a role here, but only its own Core role: every grid child carries colPos = -1 regardless of which container or cell it belongs to, and that sentinel is what the query filtering below uses to decide whether a row is a page-level, top-of-list record or only appears nested inside its container's expanded row.

How the three pieces fit together 

Once nestingInListModule is enabled, three separate mechanisms combine to produce the nested list, none of them sufficient on its own:

Mechanism Role
ModifyDatabaseQueryForRecordListingListener (PSR-14 event listener) Adds colPos != -1 to the List module's base query for tt_content, so grid children do not also appear as their own top-level rows.
Xclass\DatabaseRecordList / Xclass\DatabaseRecordList12 Renders each gridelements_pi1 container with an expand and collapse control (contentCollapseIcon(), implemented on Hooks\DatabaseRecordList and called directly, not through a hook point) and, when expanded, lists its children indented beneath it, grouped by tx_gridelements_columns.
ModifyRecordListElementDataEvent / ModifyRecordListElementDataListener Dispatched per row by the XCLASS to attach the data the template needs to know a row is expandable and to find its children: _CONTAINER_COLUMNS_, _EXPANDABLE_, _EXPAND_ID_, _EXPAND_TABLE_, _LEVEL_ and _CHILDREN_. _CHILDREN_ itself is fetched through GridElementsHelper::getChildren(), the same tx_gridelements_container query used everywhere else in Grid Elements, see Data Model.

Why this needs an XCLASS at all 

The query filtering and the per-row data are both handled through PSR-14 events, consistent with the rest of Grid Elements, see Extension Points. Building the actual nested, expandable row output is not: TYPO3 Core's List module does not currently expose an event or hook for restructuring how rows are grouped and rendered relative to each other, only for adjusting query conditions, header columns and per-row actions. XCLASSing DatabaseRecordList is how Grid Elements reaches that part of the rendering; it is the one place in the codebase this happens, and it only takes effect for a project that has opted in, see Extension Points for the caution around extending it further.

Upgrading & Compatibility 

This section covers what changes about Grid Elements' own data across a TYPO3 version upgrade, the tools available to repair that data if something goes wrong, and what does (and does not) differ in restriction enforcement between the TYPO3 versions this branch supports. It is not a changelog; historical release notes belong in the extension's own changelog, not here.

Upgrade Wizards The data-repair commands available for Grid Elements' own fields, and when a project actually needs them.
Data Compatibility Across TYPO3 Versions The colPos signed/unsigned issue and why it matters specifically during a TYPO3 Core version upgrade.
Restriction Enforcement Across Versions Whether allowed/disallowed/maxitems enforcement differs between the TYPO3 versions this branch supports.

Upgrade Wizards 

Grid Elements does not register a TYPO3 Install Tool upgrade wizard (UpgradeWizardInterface) for its own data; there is no entry for it in Admin Tools > Upgrade. The mechanisms that exist for repairing Grid Elements' own fields after something has gone wrong are the two data consistency tools documented in full at Data Consistency Tools: the Col Pos Fixer and the Number Of Children Fixer, each available as both a TYPO3 CLI command and a classic Scheduler task.

No structural migration between Grid Elements versions 

The absence of a registered upgrade wizard is not only about repair tooling; it also reflects that upgrading Grid Elements itself has not required one. Grid Elements' persisted structure, tx_gridelements_container as the parent relation, tx_gridelements_columns as the structural cell, and tx_gridelements_children as the maintained IRRE counter, see Data Model, has remained backward-compatible across Grid Elements versions. An existing Grid Elements structure continues to be understood by a newer Grid Elements version without being converted into a replacement structural storage model.

This is distinct from the two other mechanisms on this page and in this section:

  • Structural migration would convert existing data from one structural model to another. Grid Elements has not required this between its own versions.
  • Repair and consistency tools, the Col Pos Fixer and Number Of Children Fixer described below, restore specific field values after an external interference. They repair values within the existing structural model; they do not convert it into a different one.
  • Compatibility-related Upgrade Wizards, see Data Compatibility Across TYPO3 Versions, address how Grid Elements' data interacts with a given TYPO3 Core version, such as the colPos schema definition. This is not a statement that no TYPO3 Core-level upgrade wizard is ever relevant to a project using Grid Elements, only that none is required to migrate the Grid Elements structure itself.

When they are actually relevant 

Both tools exist for exceptional states, not as a routine step in every upgrade. The Col Pos Fixer's own docblock names the scenario that makes it relevant specifically to upgrading: a TYPO3 Core major-version upgrade running while Grid Elements is temporarily uninstalled or inactive, so its ext_tables.sql schema definition is not part of the active set a schema migration compares against, and the migration applies Core's unsigned colPos definition instead of Grid Elements' own. This is purely a database schema mechanism, see Data Compatibility Across TYPO3 Versions; it is separate from this tool, which only repairs the resulting record values, not the column's SQL definition. That scenario is avoidable, not an inherent part of upgrading: Grid Elements does not need to be deactivated for a Core major-version upgrade, since each maintained version already supports both the TYPO3 version it targets and its predecessor at the same time, see Data Compatibility Across TYPO3 Versions for why. This tool exists for the case where that avoidable step happened anyway, most often through a hosting provider's own automated upgrade tooling. The Number Of Children Fixer is not upgrade-specific; it repairs a maintained counter that can drift from unrelated cut/copy/paste or drag-and-drop edge cases at any time, upgrade or not.

Neither tool needs to run automatically as part of an upgrade pipeline. Run them, via CLI or a one-off Scheduler execution, only after a known interference with tt_content such as the upgrade scenario above, or when a specific report of a wrong colPos sentinel or a wrong tx_gridelements_children count cannot be explained by normal backend editing. Both are idempotent: rerunning either one against already-correct data changes nothing.

What they modify 

Tool Modifies
Col Pos Fixer tt_content.colPos, unconditionally set back to -1 for every row that currently has tx_gridelements_container > 0. It does not touch the relation itself; if tx_gridelements_container was also lost, this tool cannot recover it.
Number Of Children Fixer tt_content.tx_gridelements_children on every gridelements_pi1 container, recomputed from an actual count of rows whose tx_gridelements_container points at it.

See Data Consistency Tools for the exact commands, task classes and SQL each tool runs.

Data Compatibility Across TYPO3 Versions 

The one piece of Grid Elements' data model that is genuinely at risk during a TYPO3 version upgrade, rather than only during ordinary editing, is colPos.

The colPos issue 

This is exclusively a database schema matter, not a TCA one. tt_content.colPos has two competing SQL column definitions, supplied by two different extensions' ext_tables.sql:

Definition SQL
TYPO3 Core (typo3/cms-frontend) colPos int(11) unsigned DEFAULT '0' NOT NULL
Grid Elements colPos int(11) DEFAULT '0' NOT NULL

Grid Elements' own ext_tables.sql deliberately declares the same column without unsigned, because it depends on two negative sentinel values, -1 for a grid child and -2 for an unused element, to place content outside the page's own Backend Layout columns, see Data Model for exactly what each value means and why. TCA plays no role in this at all; TCA describes how a field is edited and validated in the backend, not the SQL type of the database column that stores it. The signed/unsigned distinction is decided entirely by which ext_tables.sql definition for this column ends up applied to the database.

Which definition wins 

TYPO3's schema migration (Admin Tools > Maintenance, `typo3 database:updateschema`, or equivalent deployment tooling) collects the ext_tables.sql of every currently active extension and combines them into the set of column definitions it compares the live database against. As long as Grid Elements is active during that step, its unsigned-free colPos definition is part of that combined set, alongside Core's unsigned one, and Grid Elements' definition is what actually applies to the column, keeping it signed.

The failure mode only exists if Grid Elements is not active during that step: with its ext_tables.sql absent from the combined set, only Core's unsigned definition remains, the schema migration then sees the live column as merely differing from that definition, and applying it converts the column back to unsigned. This conversion must never be allowed to happen. If it does, every negative sentinel value is lost: colPos on affected grid children can no longer record that they are placed in a Grid Element column (-1) or unused (-2), so TYPO3 Core's own placement handling and backend presentation, both of which read colPos directly, interpret those records incorrectly, typically as if they were ordinary page-level elements. The parent-child relation itself, stored in tx_gridelements_container, is not affected by this and continues to identify each record as a child of its Grid Element; only what colPos displays about it in the Page and List modules is wrong. This is exactly the scenario the Col Pos Fixer's own docblock describes, see Upgrade Wizards.

There is no need to ever deactivate Grid Elements for a TYPO3 Core major-version upgrade ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Deactivating, removing, or otherwise taking Grid Elements out of the picture "for the duration of the upgrade" is not a required or sensible step, and doing it is what actually creates the risk described above; it is not something a Core upgrade forces on a project. Each currently maintained Grid Elements version is built to support both the TYPO3 version it targets and that version's immediate predecessor at the same time. This branch's composer.json states that directly: "typo3/cms-core": "^12.4 || ^13.4.7". The same installed Grid Elements version is already compatible before, during and after a project runs composer update to move its TYPO3 Core requirement from ^12.4 to ^13.4.7. There is consequently no point in that upgrade where Grid Elements needs to be absent, and no reason for its ext_tables.sql to ever drop out of the combined schema definition set. Where this has gone wrong in practice, it has been a hosting provider's own automated upgrade process deactivating extensions it did not recognize as compatible without checking their actual supported-version range, not a limitation of Grid Elements itself or a real requirement of the TYPO3 upgrade process.

Keep Grid Elements active throughout the upgrade, and this entire failure mode does not arise. Back up ``tt_content`` before a TYPO3 Core major-version upgrade regardless, as ordinary precaution. If the column does get converted back to unsigned, for example because a provider's tooling deactivated the extension without being asked to, restore the colPos sentinel with the Col Pos Fixer described at Data Consistency Tools, which can repair the sentinel for any row that still has its tx_gridelements_container intact. It cannot recover the relation itself if that was also lost; a backup is the only recovery path for that case.

Applying schema migration suggestions safely 

Keeping Grid Elements active removes the cause of this specific problem, but the schema migration step itself deserves a general safety rule regardless, for colPos and every other field alike. TYPO3's schema migrator always groups its suggestions into distinct kinds, both in Admin Tools > Maintenance > Analyze Database Structure and in typo3 database:updateschema: adding new tables, fields and indexes; changing existing ones; and removing ones no active extension declares anymore. Adding is always safe to apply without reviewing each suggestion individually, since it only creates something that did not exist before and cannot destroy existing data.

Changing or removing a field or table should be the very last thing applied during an upgrade, and only after reviewing exactly what each suggestion does; never apply a batch of "change" or "remove" suggestions unread. This is precisely the mechanism that would revert colPos to unsigned: once Core's is the only active definition for that column, it does not appear as a new field to add, it appears as a suggested change to an existing one. A project that reviews change suggestions individually, rather than accepting all of them at once, would see and reject exactly that change to colPos, independent of whether Grid Elements stayed active throughout the upgrade.

What is, and is not, version-specific here 

This branch supports TYPO3 ^12.4 || ^13.4.7 simultaneously, not one or the other. Grid Elements' signed colPos schema definition and the DataHandler logic that depends on it are the same code for both supported versions; there is no version-specific colPos behavior within the range this branch targets, and no version-specific reason to deactivate it either. The risk described above is entirely a consequence of Grid Elements being made inactive during a schema migration step; it is unrelated to, and not required by, moving between any two TYPO3 Core versions this branch already supports.

Restriction Enforcement Across Versions 

Restrictions, Validation & Permissions documents the four layers that decide what an editor is allowed to place into a grid cell or page column, in full. This page only adds the version dimension: does any of that differ between the TYPO3 versions this branch supports?

Server-side enforcement: confirmed identical 

The server-side enforcement layer, Classes/Hooks/DataHandler.php and Classes/Helper/RestrictionGuard.php, contains no TYPO3-version-conditional code. The same allowed/disallowed/ maxitems checks, in processDatamap_beforeStart() and processCmdmap_beforeStart(), run identically regardless of which supported TYPO3 version (^12.4 or ^13.4.7) a project runs. There is no known gap between them for this enforcement layer.

Scope of this statement 

This confirms behavior for the current codebase on this branch only, supporting TYPO3 ^12.4 and ^13.4.7. It makes no claim about restriction-enforcement behavior on other maintained branches of the extension; earlier branches maintain their own, independent file set and are out of scope for this documentation.

FAQ & Troubleshooting 

FAQ 

Question: What is the recommended way to create grid definitions?

Answer: Even though a grid definition can be created and edited entirely as a record through the Grid Wizard, the recommended approach for a real project is a file-based definition with a unique, hand-chosen identifier rather than an autoincremented record ID. A file-based definition works identically across every environment of a project, can be put into version control and deployed like any other project configuration, and does not depend on a database record having already been created with the same ID somewhere else. See TSconfig and Grid Wizard, which reads and writes that same TSconfig regardless of where it is stored.

Question: Is Grid Elements just another container extension?

Answer: Container behavior, a parent element that holds and structures children, is part of what Grid Elements does, but it is not the whole of it. Grid Elements is a Structure-first Authoring system that includes container behavior as one part of a substantially broader architecture; a dedicated container-only extension such as EXT:container implements one particular, useful container-processing model within that broader space, not a feature-equivalent replacement. See Grid Elements and EXT:container for the detailed technical comparison, including scope, data model, DataProcessing, localization and integration mechanisms.

Question: How do I connect my templates to the structure Grid Elements builds?

Answer: The recommended, modern approach is the GridChildrenProcessor DataProcessor, see DataProcessing. Wired up in TypoScript like any other data processor, it hands a structured Grid Elements data model to Fluid, which project-specific templates and partials then render, see Rendering Architecture for how that fits together as a whole.

The original TypoScript rendering path is deprecated but remains available as a supported legacy path for existing projects, see Legacy TypoScript Rendering. It can be connected the same way a page template is, through TypoScript's TEMPLATE or FLUIDTEMPLATE objects, and it exposes its legacy virtual fields to Fluid-based templates the same way it exposes them to TypoScript-based ones, so this path does not require choosing TypoScript over Fluid either.

Question: Why is FlexForm not recommended for building new content elements?

Answer: FlexForm data is stored as a single XML blob rather than as normal, individually queryable database fields. That makes it hard to collect or aggregate that content, for example while building a teaser listing or another kind of cross-page collection, since there is no normalized data structure to query against.

Question: How should I build a new content element with individual, structured input fields instead?

Answer: Register a new CType on tt_content through the TYPO3 TCA API, since that table already provides most of the fields a project is likely to need. Add the TCA structure and the new content type via an extension, provide TypoScript or Fluid for its frontend output, and it behaves like any other content element, including inside a Grid Element.

Question: Can I place a Grid Element inside another Grid Element?

Answer: Yes. A Grid Element container is itself a regular tt_content record, using the gridelements_pi1 CType, so it is placed exactly the way any other content element is placed, and it can hold and nest further Grid Elements without a depth limit, see Nesting.

Question: Why does a drag-and-drop action reload the page, when TYPO3 Core's own don't?

Answer: When several editors work on the same page's content at the same time, TYPO3 Core only shows a change once someone opens that particular element's own edit form; nothing signals a change made elsewhere on the same page. Reloading after a drag-and-drop action avoids the confusion that would otherwise build up in a page with active structural editing by several editors at once. As long as TYPO3 has no general content-locking mechanism of its own, Grid Elements keeps this behavior.

Troubleshooting 

A grid cell that should be available doesn't show up when placing content.

Check the cell's allowed/disallowed and maxitems restrictions first; a full cell or a disallowed CType is filtered out of both the Drag-In Wizard and the New Content Element Wizard before an editor ever sees it, see Restrictions, Validation & Permissions. If the whole grid is missing rather than one cell, check that the Grid Element's own Backend Layout selection actually resolves, either a database-stored CE Backend Layout or file-based Page TSconfig, see Grid Wizard and TSconfig.

Content elements have disappeared after a grid layout was changed.

This is usually the "unused elements" state, not data loss: when a layout change removes the column an element used to occupy, Grid Elements moves it to colPos = -2 and saves its previous value in backupColPos rather than deleting it, see Data Model. Add back a matching column, or a page-level column configured with colPos = -2, to see and recover it. If elements have moved to column 0 instead, see the colPos sentinel note below.

A grid child's ``colPos`` looks wrong in the Page or List module, even though it is still shown as part of its Grid Element.

This is the signed/unsigned colPos schema issue, most often seen after a TYPO3 Core version upgrade, see Data Compatibility Across TYPO3 Versions. It affects only colPos, the field TYPO3 Core's own placement handling and backend presentation read; it does not change the actual parent relation, tx_gridelements_container, which is why the child still shows up correctly wherever rendering or editing reads that relation directly. If tx_gridelements_container is still correct on the affected rows, the Col Pos Fixer described at Data Consistency Tools restores the -1 sentinel.

Nesting a Grid Element inside another doesn't seem to work, or seems artificially limited.

Structural nesting itself has no depth limit and needs no separate setting to enable, see Nesting. If children of a nested container are missing from rendered output specifically, check the DataProcessor's recursive option: it controls how many additional levels are pre-fetched in a single processing pass and is a data-eagerness setting, not a nesting toggle, see dataProcessing.123.default.options.recursive. Fluid template recursion, the partial chain re-entering itself for a nested Grid Element, is a third, independent thing again, also documented on Nesting.

The ``children`` variable in a Fluid template is empty, or not grouped the way expected.

Confirm GridChildrenProcessor is actually wired into the relevant TypoScript dataProcessing stack, see DataProcessing. If it runs but the shape of children is unexpected, check respectColumns and respectRows: with both off, the result is a genuinely flat list; respectColumns alone makes children a column-indexed matrix; respectRows additionally indexes it by row, with the column-indexed arrays nested inside, see Reference.

A grid definition set in one place doesn't seem to apply, or gets overridden.

Grid definitions are page TSconfig, and page TSconfig cascades down the page tree the same way as any other page TSconfig: a definition set closer to the affected page wins over one set further up the rootline. Check for a more specific override before assuming the definition itself is wrong, see Where Grid Elements uses TSconfig.

Where is a Grid Element's layout actually stored? I can't find it as a file.

By default, the Grid Wizard stores a layout in a CE Backend Layout database record. That is not a limitation of the wizard, the same TSconfig construct can also be copied into file-based Page TSconfig under tx_gridelements.setup for version control, see Grid Wizard and TSconfig.

An editor was still able to place something a restriction should have blocked.

Distinguish what filtered the UI from what was actually persisted: UI-level filtering (the New Content Element Wizard, the Drag-In Wizard, and the itemsProcFunc implementations behind their form fields) determines only what looks convenient to pick and is not itself a security boundary, since its restriction data can be read from request parameters. What is actually accepted is decided separately, server side, by the DataHandler hooks and RestrictionGuard. See Restrictions, Validation & Permissions for the full layer breakdown before assuming a restriction was bypassed.

A translated page's Grid Element children don't match the default language, or got detached after translating.

This depends on whether the page uses TYPO3's connected (Translate) or free (Copy) localization mode, which interact differently with a Grid Element's own structure and its children, see Localization.

Sponsoring 

The development of Grid Elements was funded by T-Systems, Cybercraft and via a crowd funding project with participation of 99 supporters. In addition, this new version is now managed by Coders.Care and was supported by additional sponsors in a private access phase.

Inspiring people to share 

We strongly believe in the principles of Open-source software, which is why we share Grid Elements with you completely free of charge.

Still even contributors to Open-source projects have to make a living somehow, so even though you are not obliged to do so, you should consider sharing a small part of the money you might be earning with this extension in return. This way we can make sure to still provide the TYPO3 community with better extensions and services in the future.

Thanks in advance for your support.

How to support our efforts? 

Get one of the Service Level Agreements offered together with our fellow coders at Coders.Care. This helps keep Grid Elements and other covered extensions maintained, with a defined response time for support requests.
A service which allows you to become one of our Patrons on a monthly basis. Depending on the tier you choose, this can include a personal mention in the next release or the option to submit a feature request.
You don't want to sponsor monthly? Don't worry, you can also support us via PayPal with any desired amount you find reasonable.
Sometimes you want to give it a personal touch. When you want to thank Joey and Petra with a nice Single Malt you should take a look here. Slàinte mhath!

The Agreement 

Summarized from the Coders.Care blog post Service Level Agreements for TYPO3 Extensions, which explains the reasoning behind these agreements in more detail.

Enabling companies, developers and the community to join forces and thrive 

The benefit for sponsors agreeing to a service level is defined by reliability and responsiveness, and by earlier access rather than exclusive access: every improved version still becomes public for the whole community in the end, sponsors simply receive it sooner, depending on the level they choose.

Developers who take part commit to publishing their fixes and changes to the covered extensions, so the wider pool of developers involved can continue to maintain them together. This spreads maintenance across more than one person, reducing the risk that an extension is left without a maintainer.

Collaboration with the TYPO3 Core and security teams helps keep the covered extensions aligned with current TYPO3 Core versions, and lets core-related issues affecting extension behaviour be identified and addressed more quickly.

Sitemap