Blocks

Layout Block

The Layout block (vextroLayout) is a first-party block automatically injected into every blocks schema. It provides three layout modes — stack, flex, and grid — for organizing child blocks visually in the admin editor.

Automatic availability

When you call createVextroBlocksSchema, the Layout block is automatically included. You do not need to define it:

import { createVextroBlocksSchema, defineVextroBlock, f } from "vextro";

const hero = defineVextroBlock({
  slug: "hero",
  label: "Hero",
  tableName: "heroBlocks",
  fields: { heading: f.text({ required: true }) },
});

// Layout is automatically available — no need to define it
const { tables } = createVextroBlocksSchema({
  definitions: [hero],
});

Authors will see "Layout" in the block palette alongside your custom block types.

Layout modes

Stack (default)

Children are arranged vertically in a single column. Each child takes the full width. Insertion points appear between children for precise placement.

Flex

Children are arranged horizontally with flex-wrap behavior. Useful for side-by-side arrangements like a two-column hero + sidebar pattern. Options include alignment, justify, wrap, and direction.

Grid

Children are placed into cells of a configurable grid. Each cell holds at most one block. New rows are added automatically when all cells in the current rows are filled.

Grid options:

  • Columns — Number of columns (1–12, default 2). Supports responsive overrides per breakpoint.
  • Gap — Space between cells (0–24 scale, 0.25rem per unit). Supports responsive overrides per breakpoint.

Responsive options

Both Gap and Columns support per-breakpoint overrides. In the options panel, switch to a breakpoint tab (SM, MD, LG, XL) and set a different value. The base value applies at all sizes; breakpoint overrides apply at that width and above (mobile-first).

BreakpointMin width
BaseAll sizes
SM640px
MD768px
LG1024px
XL1280px

For example, you might set a 1-column grid at base and override to 2 columns at MD and 3 columns at LG, giving mobile users a single-column stack that expands to a multi-column grid on larger screens.

Nesting

Layout blocks support nesting — a Layout can contain other Layout blocks, up to the configured maxDepth. This enables complex page structures like:

Layout (grid, 2 columns)
├── Cell R1C1: Hero block
├── Cell R1C2: Layout (stack)
│   ├── Rich Text block
│   └── Call to Action block
└── Cell R2C1: Image block

How grid placement works

In grid mode, each block is assigned to a specific cell. The Layout stores a gridZones map alongside its children array — children is the ordered list of all nested blocks, and gridZones maps cell positions (like r0c0 for row 1, column 1) to the block in that cell.

You don't need to manage this directly — the admin editor handles cell assignment automatically when you add, move, or drag blocks between cells.

Valid explicit placements are preserved when the editor loads and saves a document. Changing a block's content or moving another block does not compact the grid or silently reassign occupied cells. When a block is moved into an occupied cell, the two placements swap; moving into an empty cell leaves the source cell empty.

Reordering

Blocks within a Layout can be reordered in two ways:

  1. Keyboard — Use the actions menu or context menu (right-click) to Move Up / Move Down. In grid mode, use "Move to cell" to relocate a block to any occupied or empty cell.
  2. Drag and drop — Grab the drag handle and drop onto another position or grid cell. DnD is an accelerator; keyboard access provides full parity.

Drag and drop works across nesting levels. You can drag a block from one Layout into another, or from a nested Layout up to the root level.

Reserved slug

The slug vextroLayout and table name vextroLayoutBlocks are reserved. If your app defines a block with either of these identifiers, schema generation will throw an error.

Previous
Deletion Lifecycle