Layout is Redactor’s way to put content into columns — side-by-side text, images, or any other blocks. A layout is a parent block with one or more column children; each column holds normal blocks (text, heading, image, …).
Use layouts when you need multi-column articles, feature grids, or framework-based rows (Bootstrap, Tailwind CSS).
Add a layout in the editor#
- Click Add (+) in the addbar, or use the Layout toolbar button.
- Pick a preset from the dropdown (two columns, three columns, 60/40, …).
- Click inside a column and add content as usual.
Columns show a dashed outline in the editor so you can see boundaries. On your published page, hide or style that outline with your own CSS.
Default presets#
Built-in layout presets live in the layouts option:
{
single: { title: '## layout.single-column ##', pattern: '100%' },
'two-columns': { title: '## layout.two-columns ##', pattern: '50%|50%' },
'three-columns': { title: '## layout.three-columns ##', pattern: '33%|33%|33%' },
'four-columns': { title: '## layout.four-columns ##', pattern: '25%|25%|25%|25%' },
'60-40': { title: '60/40', pattern: '60%|40%' },
'40-60': { title: '40/60', pattern: '40%|60%' }
}
Add your own presets by extending layouts. Replace the whole map with __replace: true (see Custom presets).
HTML markup#
Redactor recognizes layout by data-block="layout" on the wrapper and data-block="column" on each child. Column width uses inline flex-basis:
<div data-block="layout">
<div data-block="column" style="flex-basis: 50%;">
<p>Column 1</p>
</div>
<div data-block="column" style="flex-basis: 50%;">
<p>Column 2</p>
</div>
</div>
Paste or load this HTML into the editor — Redactor mounts it as layout + column blocks.
When you use a custom grid class (layout.grid), the wrapper may use your class instead of data-block="layout", but columns still use data-block="column" unless you also set layout.column.
Default CSS#
With default settings (layout.grid: false), Redactor adds rx-layout-grid to the layout wrapper:
.rx-layout-grid {
display: flex;
flex-direction: row;
gap: 24px;
}
Column width comes from flex-basis on each [data-block=column].
To make published content match the editor, use the same rules on your site — you do not need the .rx-content prefix outside the editor:
[data-block=layout],
.rx-layout-grid {
display: flex;
flex-direction: row;
gap: 24px;
}
Override layout styles#
In the editor
Scope overrides under .rx-content (or your classname) so they win over built-in rules:
.rx-content [data-block=layout] {
gap: 12px;
}
.rx-content [data-block=column] {
background: #eee;
padding: 32px;
}
See Change CSS styles.
On the published page
On pages that only display saved HTML, target layout directly:
[data-block=layout] {
gap: 12px;
}
[data-block=column] {
background: #eee;
padding: 32px;
}
layout.grid and layout.column#
The main settings for grid integration. They control:
- which CSS classes are added when a layout is created
- how Redactor recognizes existing HTML as layout/column blocks when parsing
const app = Redactor('#entry', {
layout: {
grid: 'row', // wrapper class(es)
column: 'col' // class(es) on every column
}
});
| Option | Default | Effect |
|---|---|---|
layout.grid |
false |
Uses built-in rx-layout-grid. Set a string to use your class(es) on the wrapper |
layout.column |
false |
No extra column class. Set a string to add class(es) to every column |
Default output:
<div data-block="layout" class="rx-layout-grid">
<div data-block="column" style="flex-basis: 50%;">
<p>Left</p>
</div>
<div data-block="column" style="flex-basis: 50%;">
<p>Right</p>
</div>
</div>
Custom classes:
<div class="row">
<div class="col" data-block="column" style="flex-basis: 50%;">
<p>Left</p>
</div>
<div class="col" data-block="column" style="flex-basis: 50%;">
<p>Right</p>
</div>
</div>
Redactor treats a <div class="row"> with column children as a layout block when layout.grid: 'row' is set — even without data-block="layout".
You can set layout.column alone and keep the default flex grid:
layout: {
column: 'my-column'
}
Or only layout.grid when columns have no shared class (common with CSS Grid):
layout: {
grid: 'grid grid-cols-3 gap-4'
}
Column pattern#
Each preset has a pattern — column segments separated by |:
pattern: '50%|50%' // two equal columns
pattern: '60%|40%' // asymmetric
pattern: '25%|25%|25%|25%' // four columns
Each segment becomes one column. Non-empty values (except -) are written as style="flex-basis: …":
pattern: '60%|40%'
<div data-block="layout">
<div data-block="column" style="flex-basis: 60%;"><p>1</p></div>
<div data-block="column" style="flex-basis: 40%;"><p>2</p></div>
</div>
Pattern with - — widths from CSS
When column widths come from framework grid classes (Bootstrap col, Tailwind grid-cols-*), use - as a placeholder — no flex-basis is set:
pattern: '-|-|-' // three columns, layout CSS controls width
<div class="grid grid-cols-3 gap-4">
<div data-block="column"><p>1</p></div>
<div data-block="column"><p>2</p></div>
<div data-block="column"><p>3</p></div>
</div>
pattern sets widths (flex-basis), not CSS classes. For column classes, use classname on the preset or per-column classname in JSON.
Custom presets#
Extend or replace entries in layouts. Each preset supports:
| Field | Description |
|---|---|
title |
Label in the dropdown (## layout.two-columns ## or plain text) |
pattern |
Column widths, pipe-separated |
grid |
Optional wrapper classes for this preset (overrides layout.grid when inserted) |
classname |
Optional classes added to every column in this preset |
const app = Redactor('#entry', {
layouts: {
'hero-split': {
title: 'Hero 70/30',
pattern: '70%|30%'
},
'two-columns': {
title: 'Half / half (styled)',
grid: 'grid grid-cols-2 gap-4',
classname: 'rounded-lg bg-slate-100 p-4',
pattern: '50%|50%'
}
}
});
Replace all default presets
By default, layouts is merged with built-in presets. To use only yours:
layouts: {
__replace: true,
'my-grid': {
title: 'My grid',
pattern: '50%|50%'
}
}
__replace: true replaces the whole layouts object at that level. See Settings overview.
Tailwind CSS#
Point layout.grid at your grid wrapper class and define presets with Tailwind utilities:
const app = Redactor('#entry', {
layout: {
grid: 'grid'
},
layouts: {
__replace: true,
'two-columns': {
title: 'Two columns',
grid: 'grid grid-cols-2 gap-4 mb-6',
classname: 'bg-indigo-100 p-4 rounded-lg',
pattern: '50%|50%'
},
'three-columns': {
title: 'Three columns',
grid: 'grid grid-cols-3 gap-4 mb-6',
classname: 'bg-emerald-100 p-4 rounded-lg',
pattern: '-|-|-'
}
},
custom: {
js: {
head: ['https://cdn.tailwindcss.com']
}
}
});
Inserted HTML:
<div class="grid grid-cols-2 gap-4 mb-6">
<div class="bg-indigo-100 p-4 rounded-lg" data-block="column" style="flex-basis: 50%;">
<p></p>
</div>
<div class="bg-indigo-100 p-4 rounded-lg" data-block="column" style="flex-basis: 50%;">
<p></p>
</div>
</div>
For three equal columns driven only by grid-cols-3, use pattern: '-|-|-' so Redactor does not set conflicting flex-basis.
Editor styles for Tailwind
The editor may pick up page Tailwind styles, but for a consistent editing surface inject grid utilities into the editor (iframe custom.css or your site CSS):
.rx-content .grid {
display: grid;
}
.rx-content .grid-cols-2 {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
.rx-content .grid-cols-3 {
grid-template-columns: repeat(3, minmax(0, 1fr));
}
.rx-content .gap-4 {
gap: 1rem;
}
Bootstrap#
Map layout to Bootstrap’s row/column classes:
const app = Redactor('#entry', {
layout: {
grid: 'row',
column: 'col'
},
layouts: {
__replace: true,
'two-columns': {
title: 'Two columns',
grid: 'row g-3',
pattern: '-|-'
},
'three-columns': {
title: 'Three columns',
grid: 'row g-3',
pattern: '-|-|-'
}
},
custom: {
css: ['https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css']
}
});
Output:
<div class="row g-3">
<div class="col" data-block="column"><p>1</p></div>
<div class="col" data-block="column"><p>2</p></div>
</div>
Bootstrap flex columns handle width — use pattern: '-|-' instead of percentages.
Editor styles for Bootstrap
.rx-content .row {
display: flex;
flex-wrap: wrap;
}
.rx-content .col {
flex: 1 0 0%;
}
JSON data#
Load layouts via the data option or setJson. See Set & get JSON.
With column widths
Redactor('#entry', {
data: [
{
type: 'layout',
data: {
pattern: '40%|60%',
columns: [
{
width: '40%',
children: [
{ type: 'heading', data: { level: 3, content: 'Column 1' } },
{ type: 'text', data: { content: '<p>This is a paragraph.</p>' } }
]
},
{
width: '60%',
children: [
{ type: 'heading', data: { level: 3, content: 'Column 2' } },
{ type: 'text', data: { content: '<p>This is a paragraph.</p>' } }
]
}
]
}
}
]
});
With CSS classes (no width)
Redactor('#entry', {
layout: { grid: 'row', column: 'col' },
data: [
{
type: 'layout',
data: {
grid: 'row',
columns: [
{
classname: 'col col-4',
children: [
{ type: 'text', data: { content: '<p>Column 1</p>' } }
]
},
{
classname: 'col col-8',
children: [
{ type: 'text', data: { content: '<p>Column 2</p>' } }
]
}
]
}
}
]
});
| JSON field | Description |
|---|---|
pattern |
Generate columns with these flex-basis widths |
grid |
Wrapper CSS class |
column |
Class applied to every column (same as preset classname) |
columns[] |
Explicit columns with width, classname, children |
API#
// Open layout dropdown from code
app.layout.open({}, { button: toolbarButton });
// Insert by preset pattern (uses layouts[key] params)
app.layout.insert({
pattern: '50%|50%',
grid: 'grid grid-cols-2 gap-4',
classname: 'p-4'
});
See layout module and layout block.