Column Control adds flexible layouts to Strapi 5 dynamic zones. It lets editors control how content is arranged and create custom page layouts.
released August 13, 2026
npm install strapi-column-controlColumn layouts for Strapi 5 Dynamic Zones — container → row → columns — without replacing your existing components. Map column_class on the frontend with any CSS library, framework, or custom grid.
Requires Strapi 5 (@strapi/strapi ^5.43) and Node.js 20–26.
npm install strapi-column-controlEditors add a Column control block to a Dynamic Zone. Each column has a nested blocks zone where they stack the same components you already use (headlines, buttons, images, custom types).
Article dynamic_zone
└── Column control layout (container, row, columns)
├── Column 1 (span 6)
│ └── blocks[] your components, in order
└── Column 2 (span 6)
└── blocks[] same allow-list as the parent zonecolumn_class is a numeric span (for example 6). Your frontend maps it to Bootstrap, Tailwind, CSS Grid, or any custom classes.gridColumns (default 12).blocks render in array order.From your Strapi 5 project root:
npm install strapi-column-controlEnable the plugin in config/plugins.ts:
export default {
'strapi-column-control': {
enabled: true,
config: {
gridColumns: 12, // max sum of column_class per row
},
},
};Restart Strapi. postinstall patches Content-Type Builder so you can edit Dynamic Zones that include this plugin.
| Key | Default | Purpose |
|---|---|---|
gridColumns | 12 | Maximum sum of column_class in one Column control block |
columnBlocks | auto | Optional extra UIDs for column blocks. Prefer adding types on the parent Dynamic Zone instead |
Add Column control and your block types to the parent Dynamic Zone (dynamic_zone, sections, and so on):
"components": [
"plugin::strapi-column-control.layout.column-control",
"atoms.button",
"atoms.headline-text"
]Restart Strapi. The plugin copies those UIDs into each column’s blocks zone. Editors add instances in Content Manager.
Do not edit the plugin Column component’s blocks list in CTB. That allow-list is derived from the host Dynamic Zone. Emptying it fails validation; the UI blocks editing plugin layout zones when possible.
| UID | Role |
|---|---|
plugin::strapi-column-control.layout.column-control | Layout block on the host Dynamic Zone |
plugin::strapi-column-control.layout.column | One column (span + blocks) |
| Field | Description |
|---|---|
configuration | Container width, padding, gutter, reverse on mobile |
columns | Repeatable columns |
columns.column_class | Span units; row total ≤ gridColumns |
columns.blocks | Nested Dynamic Zone (parent DZ types + optional columnBlocks) |
columns.admin_label | Private Content Manager title (auto) |
Example: 5 + 5 + 2 = 12 when gridColumns is 12. The REST JSON shape is stable across upgrades.
populate=*Document middleware deepens shallow Dynamic Zone populate so nested columns → blocks are returned.
const mapper = strapi.plugin('strapi-column-control').service('mapper');
const { gridColumns } = mapper.getGridConfig();
const { valid, violations, message } = mapper.validateColumnSpans(entry.dynamic_zone);
const mapped = mapper.mapColumnControl(section); // host maps column_class to CSS| Route | Auth | Returns |
|---|---|---|
POST /api/strapi-column-control/map | public | { configuration, mapped, classes, html } |
GET /admin/strapi-column-control/grid-config | admin | { gridColumns } |
Two columns. Each blocks array can hold several components:
{
"__component": "plugin::strapi-column-control.layout.column-control",
"configuration": {
"container_width": "default",
"container_padding": "default",
"gutter": "default",
"reverse_on_mobile": false
},
"columns": [
{
"column_class": 6,
"blocks": [
{
"__component": "atoms.headline-text",
"title": "Welcome",
"subtitle": "Blocks in this column stack top to bottom"
},
{
"__component": "atoms.button",
"button_text": "Get started",
"url": "/signup"
}
]
},
{
"column_class": 6,
"blocks": [
{
"__component": "atoms.headline-text",
"title": "Features",
"subtitle": "Any type allowed on the parent Dynamic Zone"
},
{
"__component": "atoms.button",
"button_text": "Learn more",
"url": "/docs"
}
]
}
]
}Bump the package and restart Strapi. No content migration or host-app code changes.
npm install strapi-column-control@latestRelease notes: CHANGELOG.md.
Machine-readable summary: llms.txt (included in the npm package).
Share your work with the community and get it listed in the Strapi ecosystem for everyone to discover and use.
Submit
