Layout Templates
Layouts define how regions are arranged inside the shell content area. The template field in the LayoutDescriptor selects which layout to use.
Overview
| Template | Regions | Use Case |
|---|---|---|
stack | content | Lists, tables, single-column pages |
split | content, sidebar | Detail pages with sidebar (320px aside) |
dashboard | header, content | KPI overview with header metrics row |
master-detail | master, detail | Table + side panel (320px detail) |
wizard | steps | Multi-step forms with progress indicator |
canvas | content | Visual editors (flow, form builder) |
stack
Single column, blocks stacked vertically. The simplest and most common layout.
Regions: content
{
template: 'stack',
regions: {
content: [
{ type: 'status_strip', key: 'status', data: { ... } },
{ type: 'dense_table', key: 'users', data: { ... } },
],
},
}Use cases: User lists, settings pages, single-focus views, form pages.
split
Two-column layout with a main content area and a sidebar.
Regions: content, sidebar
{
template: 'split',
regions: {
content: [
{ type: 'detail_panel', key: 'details', data: { ... } },
{ type: 'activity_timeline', key: 'timeline', data: { ... } },
],
sidebar: [
{ type: 'link_list', key: 'related', data: { ... } },
{ type: 'metric_card', key: 'stats', data: { ... } },
],
},
}Use cases: Entity detail pages, documentation with ToC, profile pages.
The sidebar is 320px wide by default and collapses below the content on mobile.
dashboard
Grid layout with a header row for metrics and a body grid for content blocks.
Regions: header, content
{
template: 'dashboard',
regions: {
header: [
{ type: 'metric_card', key: 'users', data: { ... } },
{ type: 'metric_card', key: 'courses', data: { ... } },
{ type: 'metric_card', key: 'completions', data: { ... } },
],
content: [
{ type: 'dense_table', key: 'recent', data: { ... } },
{ type: 'activity_timeline', key: 'activity', data: { ... } },
],
},
}Use cases: Dashboard overview pages, analytics summaries, admin home.
The header region renders as a responsive row of metric cards. The content region renders as a vertical stack below.
master-detail
List-detail split layout with a master list on the left and a detail panel on the right.
Regions: master, detail
{
template: 'master-detail',
regions: {
master: [
{ type: 'dense_table', key: 'list', data: { ... } },
],
detail: [
{ type: 'detail_panel', key: 'selected', data: { ... } },
],
},
}Use cases: Email-style interfaces, entity browsers, connector management.
The detail panel is 320px wide and appears when a row is selected. On mobile, it renders as a full-screen overlay.
wizard
Multi-step form layout with a progress indicator.
Regions: steps (array of step regions)
{
template: 'wizard',
regions: {
steps: [
{
key: 'basic-info',
label: 'Basic Information',
blocks: [
{ type: 'form_panel', key: 'step1-form', data: { ... } },
],
},
{
key: 'configuration',
label: 'Configuration',
blocks: [
{ type: 'form_panel', key: 'step2-form', data: { ... } },
],
},
{
key: 'review',
label: 'Review',
blocks: [
{ type: 'detail_panel', key: 'review-panel', data: { ... } },
],
},
],
},
meta: {
currentStep: 0,
},
}Use cases: Onboarding flows, multi-step configuration, setup wizards.
canvas
Full-bleed layout for visual editors. Minimal chrome, maximum canvas area.
Regions: content
{
template: 'canvas',
regions: {
content: [
{ type: 'flow_editor', key: 'workflow', data: { ... } },
],
},
}Use cases: Workflow editors, form builders, visual rule editors.
The canvas layout removes padding and allows the block to fill the entire available area. It is typically used with the flow_editor or form_builder block types.
Custom Layouts
You can register custom layout components:
import { registerLayout } from '@middag-io/react';
import type { LayoutProps } from '@middag-io/react';
function MyCustomLayout({ layout, renderBlock }: LayoutProps) {
const content = layout.regions.content ?? [];
const sidebar = layout.regions.sidebar ?? [];
return (
<div className="grid grid-cols-3 gap-4">
<div className="col-span-2">
{content.map(renderBlock)}
</div>
<div>
{sidebar.map(renderBlock)}
</div>
</div>
);
}
registerLayout('my_custom', MyCustomLayout);Then reference it in the contract:
{
template: 'my_custom',
regions: {
content: [...],
sidebar: [...],
},
}