Skip to content

Sections and Columns

The SectionBlock is the layout primitive in Templatical. Every multi-column layout is built by creating a section and placing blocks inside its columns.

Column layouts

Email best practice

Stick to 1-2 columns for most emails. Three-column layouts become cramped on mobile when columns stack vertically, and some email clients handle 3-column layouts inconsistently.

The columns property accepts one of five layout presets:

ValueDescriptionColumn widths
'1'Single column100%
'2'Two equal columns50% / 50%
'3'Three equal columns33% / 33% / 33%
'2-1'Two-thirds / one-third66% / 33%
'1-2'One-third / two-thirds33% / 66%
ts
type ColumnLayout = '1' | '2' | '3' | '2-1' | '1-2';

Creating sections

Use createSectionBlock from @templatical/types:

ts
import {
  createSectionBlock,
  createTitleBlock,
  createParagraphBlock,
  createImageBlock,
} from '@templatical/types';

// Empty two-column section
const section = createSectionBlock({ columns: '2' });

// Section with pre-populated columns
const hero = createSectionBlock({
  columns: '1-2',
  children: [
    [createImageBlock({ src: 'https://cdn.example.com/logo.png', width: 120 })],
    [
      createTitleBlock({ content: '<h1>Welcome</h1>', level: 1 }),
      createParagraphBlock({ content: '<p>Get started in minutes.</p>' }),
    ],
  ],
});

The children array

children is a Block[][] -- an array of arrays. Each inner array represents the blocks inside one column, ordered left to right.

ts
// For a '2' layout:
section.children = [
  [blockA, blockB],  // Left column
  [blockC],          // Right column
];

// For a '3' layout:
section.children = [
  [blockA],  // Left
  [blockB],  // Center
  [blockC],  // Right
];

The length of children must match the number of columns in the chosen layout. When you change columns, the editor automatically adjusts children -- merging excess columns into the last column or adding empty arrays for new columns.

Adding blocks to columns

To programmatically add a block to a specific column:

ts
// Add a button to the second column (index 1)
section.children[1].push(
  createButtonBlock({
    text: 'Learn More',
    url: 'https://example.com/docs',
  }),
);

Nesting

Sections cannot be nested inside other sections. Each section sits at the top level of the template's block list. Within a column, you can place any non-section block type: titles, paragraphs, images, buttons, tables, custom blocks, and so on.

Responsive behavior

On desktop, columns render side-by-side at their defined widths. On smaller screens, columns stack vertically in source order (left column on top) — MJML's default.

stackOnMobile opts out. Omit it or set true to keep stacking. false wraps the columns in <mj-group> so they stay side by side on mobile. The section toolbar exposes the toggle on multi-column sections. A one-column section never emits mj-group.

ts
const row = createSectionBlock({ columns: '2', stackOnMobile: false });

You can use the visibility property on individual blocks within columns to show or hide content per breakpoint:

ts
const block = createParagraphBlock({
  content: '<p>Desktop only sidebar content</p>',
});

block.visibility = {
  desktop: true,
  mobile: false,
};

See Styling for more on block visibility.

Section-level styles

Sections support the same BlockStyles as other blocks. Common use cases include setting a background color or padding on the entire row:

ts
const section = createSectionBlock({ columns: '1' });

section.styles = {
  backgroundColor: '#f8fafc',
  padding: { top: 32, right: 24, bottom: 32, left: 24 },
};

Sections also accept a borderRadius (in px, a section-specific property) for rounded corners — useful for a card-style section on a contrasting background:

ts
const card = createSectionBlock({ columns: '1', borderRadius: 12 });

card.styles.backgroundColor = '#ffffff';

Outer frame (wrapper)

To frame a section in a full-width colored band — e.g. a white card sitting on a blue background — set wrapper. It renders an mj-wrapper around the section, with its own background, padding, and corner radius:

ts
const card = createSectionBlock({ columns: '1', borderRadius: 12 });
card.styles.backgroundColor = '#ffffff';

card.wrapper = {
  backgroundColor: '#1e3a8a',
  padding: { top: 24, right: 24, bottom: 24, left: 24 },
  borderRadius: 16,
};

The wrapper's padding is what makes the band show around the section. In the editor, enable it from the Wrapper panel in the section toolbar. The wrapper is a styling layer, not a container — sections still cannot be nested.