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).
| Breakpoint | Min width |
|---|---|
| Base | All sizes |
| SM | 640px |
| MD | 768px |
| LG | 1024px |
| XL | 1280px |
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:
- 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.
- 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.