Pages
Pages are a built-in form structure that divides a form into multiple navigable sheets. Unlike field types such as section, pages are a first-class form primitive — they live at the top level of every form definition rather than inside the field tree.
Every form definition has a pages array. A form with a single page behaves identically to a traditional single-scroll form. A multi-page renderer can show page-title tabs at the top, Previous / Next controls at the bottom, or both.
Page Entry Properties
Each entry in the pages array has the following shape:
| Property | Type | Required | Description |
|---|---|---|---|
id | string | Yes | Unique identifier for this page |
title | string | No | Display label for the page (defaults to "Sheet N" in the builder) |
autoAdvance | boolean | No | Reserved — automatically advance to the next page when all fields are answered. Not yet active in the renderer. |
fields | FieldDefinition[] | No | The fields rendered on this page. May include section fields to group questions. |
Answer Format
Pages store no answers. All answers live on individual fields within each page's fields array.
Schema Example
Single-page form
A form with one page behaves exactly like a classic single-scroll form — no navigation UI is shown.
id: intake-form
title: Patient Intake
pages:
- id: page_1
title: Demographics
fields:
- id: first_name
fieldType: text
question: First Name
required: true
- id: dob
fieldType: text
question: Date of Birth
inputType: date
Multi-page form
A form with two or more pages can show top page tabs and/or a navigation bar (← Previous · X / Y · Next →) at the bottom of the renderer.
id: intake-form
title: Patient Intake
pages:
- id: page_1
title: Demographics
fields:
- id: first_name
fieldType: text
question: First Name
required: true
- id: page_2
title: Medical History
fields:
- id: allergies
fieldType: longtext
question: List any known allergies
Renderer Behavior
Implemented in RendererBody and PageNavigator.
Configure navigation on the renderer component:
<EsheetRenderer
formDataInput={formDefinition}
topNavigation={true}
bottomNavigation={false}
validateNavigation={true}
/>
| Prop | Default | Behavior |
|---|---|---|
topNavigation | false | Shows page-title tabs above the active page. |
bottomNavigation | true | Shows Previous / page count / Next controls below the active page. |
validateNavigation | true | Blocks forward movement from either navigation surface when required fields on the current page are unanswered. |
Navigation validation is separate from submit validation. Turning validateNavigation off allows page changes but does not allow an invalid form to submit or change the behavior of getValidResponse().
| Condition | Behavior |
|---|---|
pages.length === 1 | Fields render directly, no navigation UI shown |
pages.length > 1 | PageNavigator renders the enabled top tabs and/or bottom bar |
| First page | "Previous" button is disabled |
| Last page | "Next →" button is replaced with "Last page" text |
Visibility rules (rules) on individual fields are evaluated per page — hidden fields are excluded from the rendered output on that page.
Builder Behavior
The Canvas panel header displays a Sheets bar when pages are present.
- Tabs: each page appears as a labeled tab. Clicking switches the active page.
- Add: click + Sheet to append a new blank page.
- Delete: click Delete sheet to remove the active page. Disabled when only one page remains (minimum 1 page enforced).
- Reorder: drag a tab left or right to change page order.
- Rename: double-click a tab, or click the ✎ pencil icon on the active tab, to edit the page title inline.
Form Store API
These methods on FormStore manage pages programmatically:
// Add a new blank page (optionally with a title and insertion index)
form.getState().addPage({ title?: string; index?: number }): string
// Remove a page and all its fields. Returns false if only 1 page remains.
form.getState().removePage(pageId: string): boolean
// Move a page to a new index.
form.getState().movePage(pageId: string, toIndex: number): boolean
// Update a page's display title.
form.getState().updatePageTitle(pageId: string, title: string): boolean
// Set the auto-advance flag for a page (reserved; not yet active in renderer).
form.getState().setPageAutoAdvance(pageId: string, autoAdvance: boolean): boolean
Constraints
- Minimum 1 page:
removePagereturnsfalseand is a no-op when the form has only one page. - No nesting: pages cannot be placed inside
sectionfields or inside other pages. Pages exist exclusively at the form root level. - No field-level pages: pages are not a
fieldType— they cannot be added through the fields API (addField) and do not appear in the field toolbox.