Layout Schema
layout describes page structure for resources, sections, tabs, wizard steps, and page surfaces.
Layout schema should describe UI structure only. It should not contain business logic.
The current core renderer uses page and sections to stack multi-resource schemas, and uses tabbed navigation for multi-resource schemas when the layout is not stacked. For forms, tabs renders tabbed field groups and wizard renders stepper-style fields from layout.steps.
Layout properties
| Property | Type | Purpose |
|---|---|---|
type | "page" | "tabs" | "wizard" | "sections" | Layout style. |
tabs | array | Tab definitions. |
steps | array | Wizard step definitions. |
sections | array | Section definitions. |
className | string | Styling hook. |
Page layout
layout.className applies to the root layout container regardless of layout.type or whether resources[] is present. layout.type itself only matters for multi-resource resources[] stacking/tabs and for form-level tabs/wizard layout — for a single-resource schema like this one, the "hide the table while the form is open" effect comes entirely from form.mode: "page", not from layout.type.
schemas/users/page-layout.schema.ts
export const pageLayoutSchema = {
version: "1.0.0",
resource: "users",
layout: {
className: "space-y-6"
},
table: {
columns: ["name", "email", "status"]
},
form: {
mode: "page",
fields: ["name", "email", "status"]
}
} as const;Tabs layout
schemas/workspace/tab-layout.schema.ts
export const tabLayoutSchema = {
version: "1.0.0",
resource: "workspace",
layout: {
type: "tabs"
},
resources: [
{
resource: "users",
title: "Users",
table: { columns: ["name", "email"] },
form: { mode: "modal", fields: ["name", "email"] }
},
{
resource: "roles",
title: "Roles",
table: { columns: ["name", "description"] },
form: { mode: "modal", fields: ["name", "description"] }
}
]
} as const;Multi-resource tabs are generated from resources[]. The child title or resource value provides the tab label.
Wizard form layout
schemas/onboarding/wizard-layout.schema.ts
export const wizardLayoutSchema = {
version: "1.0.0",
resource: "onboarding",
layout: {
type: "wizard",
steps: [
{ key: "account", title: "Account", fields: ["name", "email"] },
{ key: "access", title: "Access", fields: ["roleId", "status"] }
]
},
form: {
mode: "page",
fields: [
{ name: "name", label: "Name", type: "text" },
{ name: "email", label: "Email", type: "email" },
{ name: "roleId", label: "Role", type: "select", optionLoader: "roles.options" },
{ name: "status", label: "Status", type: "select", optionLoader: "users.statusOptions" }
]
}
} as const;Section definition
Sections use this shape:
schemas/users/layout-sections.ts
export const section = {
key: "account",
title: "Account",
fields: ["name", "email"],
table: false,
form: true,
className: "space-y-4"
} as const;table: true and form: true place the root table or an always-visible create form into a single-resource root layout slot. When resources[] is present, child resource tabs and sections are generated from resources[].