Create multi-step forms by using page fields. When your form contains page fields, it automatically enters "paged mode" and renders with navigation controls.
Basic Multi-Step Form
Create a multi-step form by adding multiple page fields to your form configuration:
{
fields: [
{
key: 'account',
type: 'page',
fields: [
{ key: 'accountTitle', type: 'text', label: 'Account Information', props: { elementType: 'h3' } },
{ key: 'username', type: 'input', label: 'Username', value: '', required: true },
{ key: 'password', type: 'input', label: 'Password', value: '', props: { type: 'password' }, required: true },
],
},
{
key: 'profile',
type: 'page',
fields: [
{ key: 'profileTitle', type: 'text', label: 'Profile Details', props: { elementType: 'h3' } },
{ key: 'firstName', type: 'input', label: 'First Name', value: '' },
{ key: 'lastName', type: 'input', label: 'Last Name', value: '' },
],
},
],
}Page Properties
Each page field supports:
key(required) - Unique identifier for the pagetype: 'page'(required) - Field type identifierfields(required) - Array of child fields to render on this page
Page with Description
{
key: 'preferences',
type: 'page',
fields: [
{ key: 'newsletter', type: 'checkbox', label: 'Subscribe to newsletter', value: false },
{ key: 'notifications', type: 'checkbox', label: 'Enable notifications', value: false },
],
}Paged Mode Behavior
When your form contains page fields:
- Automatic Detection: Form automatically enters "paged mode"
- Navigation Controls: Previous/Next buttons are rendered automatically
- Validation: Users must complete required fields before advancing to the next page
- Single Page View: Only one page is visible at a time
Performance & Lazy Loading
ng-forge uses Angular's @defer blocks with smart prefetching to optimize page rendering while maintaining flicker-free navigation.
How It Works
The page orchestrator uses a 2-tier loading strategy:
Tier 1: Current + Adjacent Pages (±1)
- Render immediately using
@defer (on immediate) - Initially, only 3 pages load (current + 2 adjacent)
- Adjacent pages are fully rendered but hidden with
display: none - Ensures zero flicker when navigating forward/backward
Tier 2: Distant Pages (2+ steps away)
- Defer loading until browser is idle using
@defer (on idle) - Lazy loading optimizes initial page load
- Load automatically during browser idle time
- Once loaded, pages remain in DOM (hidden with CSS)
Benefits
// Example: User is on step 2 of 5
fields: [
{ key: 'step1', type: 'page', ... }, // ✓ Rendered (adjacent)
{ key: 'step2', type: 'page', ... }, // ✓ Visible (current)
{ key: 'step3', type: 'page', ... }, // ✓ Rendered (adjacent)
{ key: 'step4', type: 'page', ... }, // ⏳ Deferred (distant)
{ key: 'step5', type: 'page', ... }, // ⏳ Deferred (distant)
]Performance advantages:
- ⚡ Zero navigation flicker - Adjacent pages already rendered
- Faster initial load - Only 3 pages render immediately, distant pages defer until idle
- ⏱️ Better Time to Interactive (TTI) - Reduced initial JavaScript parsing/compilation
- 📱 Mobile-friendly - Lower startup cost on slower devices
Note: Once loaded, pages remain in the DOM (hidden with CSS). The primary benefit is optimizing initial load performance, not ongoing memory usage.
This optimization happens automatically - no configuration needed.
Value Structure
Pages are container fields - they don't add nesting to your form values. Fields flatten to the root level:
// Form config with pages
{
fields: [
{
key: 'page1',
type: 'page',
fields: [
{ key: 'firstName', type: 'input', value: '' },
],
},
{
key: 'page2',
type: 'page',
fields: [
{ key: 'lastName', type: 'input', value: '' },
],
},
],
}
// Resulting form value (flat structure)
{
firstName: 'John',
lastName: 'Doe',
// Note: page keys are NOT in the value
}Nesting Restrictions
Page fields can only be used at the top level of your form configuration. They cannot be nested inside:
- Other page fields
- Row fields
- Group fields
Attempting to nest pages will result in a validation error.
Allowed Children
Pages can contain:
- Leaf fields (input, select, checkbox, etc.)
- Row fields (for horizontal layouts)
- Group fields (for nested data structures)
Conditional Visibility
Pages support the logic property to conditionally skip a page (hide it from the page navigation and progression) based on form state.
{
key: 'businessDetails',
type: 'page',
label: 'Business Details',
logic: [{
type: 'hidden',
condition: {
type: 'fieldValue',
fieldPath: 'accountType',
operator: 'notEquals',
value: 'business',
},
}],
fields: [
{ key: 'companyName', type: 'input', label: 'Company Name', value: '' },
{ key: 'taxId', type: 'input', label: 'Tax ID', value: '' },
],
}When a page is hidden, it is excluded from the multi-step navigation — users skip directly past it. Only 'hidden' is supported as a logic type on containers.
For all available condition types and operators, see Conditional Logic.
CSS Classes
Page fields use these classes for styling:
.df-page- Applied to the page container.df-page-visible- Applied to the currently visible page.df-page-hidden- Applied to hidden pages.df-page-field- Applied to the page field component
Next Steps
- Form Arrays — Create repeating sections with add/remove controls
- Dynamic Behavior — Conditional logic, value derivation, and form submission
- Form Rows — Arrange fields side-by-side within pages