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.
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.
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.
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).
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.
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:
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.
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.
Note
One of those changes turns tt_content.colPos into a signed
column, since Grid Elements stores negative sentinel values there
for its own children. Applying that change is required. Never
revert it with a later schema fix or manual migration: doing so
prevents those negative sentinel values from being stored, which
makes TYPO3 Core's own placement handling and backend presentation
misinterpret affected records, even though the underlying
tx_gridelements_container parent relation itself remains intact.
See Data Model for what those sentinel values mean and how to
recover if this happens.
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.
Include the recommended static template
Grid Elements ships two static templates. Open the TypoScript module,
edit the site's root template record, and on the Includes tab, under
Include static (from extensions), add:
Gridelements w/DataProcessing (recommended)
This is the current, supported rendering path, built on
GridChildrenProcessor and Fluid, see Rendering for how it
works. The other entry, Gridelements (deprecated), wires up the
older Plugin\Gridelements TypoScript path; it is still functional
for existing projects but not recommended for new ones, see
Legacy TypoScript Rendering. Include one of the two, not both.
The recommended static template builds on lib.contentElement,
which TYPO3's own Fluid-based content rendering provides. Make sure a
static template that defines it, ordinarily Fluid Content Elements
(fluid_styled_content) or a sitepackage that supplies the
equivalent, is included before Grid Elements' own static template.
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.
Note
This assumes Grid Elements is installed, active, and its
recommended static template is included, see Installation.
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:
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:
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.
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.
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'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 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!
Note
Grid layouts stored as TSconfig, and Grid Elements' own CE layout
records, historically wrapped this configuration in a
backend_layout block, the same wrapper page-level Backend
Layouts use:
Grid Elements still recognizes and unwraps this block for
compatibility with configuration written that way. It is not the
current way to write a new grid definition; write the keys directly
under config as shown above.
Fill in the rows
rows is a plain array with one numeric key per row, even a row
that will stay empty:
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:
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.
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:
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:
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:
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:
Saving the layout to the CE backend layout record
With every cell named and assigned, the layout is complete:
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.
Note
FlexForm sections, repeatable groups of fields, are not
resolved by either rendering path's FlexForm handling. Fields
outside a section are read and made available as described below;
fields inside a section are not.
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.
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.
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.
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(), simplifiedSELECT * 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/:
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.
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(), simplifiedif ($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:
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_pi1CType. 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 shortcutCType
(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
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 = -1WHERE 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.
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:
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 recommended path: DataProcessing and Fluid
GridElementsTeam\Gridelements\DataProcessing\GridChildrenProcessor
is a standard TYPO3 DataProcessorInterface implementation, wired up
in TypoScript like any other data processor. It queries
tt_content for the records whose tx_gridelements_container
matches the current container, the same relation described in
Data Model, resolves each child's FlexForm and backend layout
data as configured, and groups the result into the variable a Fluid
template receives, either a flat list or a rows/columns matrix built
from the container's own grid layout. It hands that structured data to
Fluid; it does not decide how it is displayed. See DataProcessing
for how to configure it and Reference for its
options.
Fluid then renders that data through a small set of partials
(Container, Rows, Columns, Child) that walk the
structure the processor built. A child that is itself a Grid Element
re-enters the same partial chain, which is the Fluid template
recursion described in Nesting. This page does not repeat that
explanation; the distinction between structural nesting, the
processor's recursive option, and this Fluid recursion is
canonical there.
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_REGISTER5 {
tt_content_shortcut_recursive.field = recursive
}
10 = USER10 {
userFunc = GridElementsTeam\Gridelements\Plugin\Gridelements->user_getTreeList
}
20 = CONTENT20 {
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.
This guide outlines the configuration of the GridChildrenProcessor, its options, and examples of corresponding templates and partials for rendering grid-based layouts in TYPO3.
Recommended approach vs. legacy path
The GridChildrenProcessor represents the recommended, Fluid-based approach to handling grid layouts in TYPO3, offering significant advantages over the deprecated legacy path built on the Gridelements plugin. See Rendering Architecture for how the two relate.
Legacy path (deprecated, not recommended for new projects):
The original Grid Elements implementation relied on the Gridelements->main userFunc in TypoScript, which processed grid children internally and rendered them using pre-defined TypoScript objects (COA, TEMPLATE). This method required extensive TypoScript configuration for customization and made it difficult to separate content structure from presentation.
Recommended approach with GridChildrenProcessor:
The DataProcessor approach leverages TYPO3's ContentObjectRenderer data processing capabilities to:
Separate data gathering from rendering logic
Allow full Fluid template control over the grid structure
Provide specific rendering options per grid layout type
Enable recursive processing of nested grids with clean template code
Offer greater flexibility through configuration options like respectColumns and respectRows
Integrate seamlessly with other TYPO3 data processors
This approach significantly improves maintainability and template reusability, and follows TYPO3's current best practices for content rendering.
Example Configuration
Below is an example GridChildrenProcessor configuration:
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.
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.
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.
Renders individual child elements. If the child element itself is a container, this partial recursively calls the Container partial to render its children.
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.
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.
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
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
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
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
Note
This rendering path is deprecated, not recommended for new
projects. It is retained for existing projects that already render
through it and remains fully functional. New implementations should
use DataProcessing and Fluid instead, see
Rendering Architecture for how the two paths compare.
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_REGISTER20 =< 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.stdheader20 = COA20 {
10 = USER10 {
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_REGISTER5 {
tt_content_shortcut_recursive.field = recursive
}
10 = USER10 {
userFunc = GridElementsTeam\Gridelements\Plugin\Gridelements->user_getTreeList
}
20 = CONTENT20 {
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.
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.
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.
Filters the New Content Element Wizard's item list against the
target cell's allowed/disallowed restrictions, see
Restrictions, Validation & Permissions.
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.
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.
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:
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.
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.
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.
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.
Related, but not a restriction-enforcement, difference
Two other backend touchpoints do differ by TYPO3 version, and are easy
to conflate with restriction enforcement because they sit nearby, but
neither one decides what is allowed or accepted:
The record-usage marking mechanism, Hooks\PageLayoutView (dead
in the versions this branch supports) versus
IsContentUsedOnPageLayoutListener (current), only affects
whether a grid child is reported as "used" for Core's own unused-content
indicators.
The List module XCLASS, Xclass\DatabaseRecordList versus
Xclass\DatabaseRecordList12, only affects how nested rows are
rendered when nestingInListModule is enabled.
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_pi1CType, 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!
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
Reference to the headline
Copy and freely share the link
This link target has no permanent anchor assigned.The link below can be used, but is prone to change if the page gets moved.