Layouts and grids

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#

  1. Click Add (+) in the addbar, or use the Layout toolbar button.
  2. Pick a preset from the dropdown (two columns, three columns, 60/40, …).
  3. 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.