Documentation Index
Fetch the complete documentation index at: https://docs.encoreos.io/llms.txt
Use this file to discover all available pages before exploring further.
Version: 1.1
Created: 2026-01-28
Last Updated: 2026-04-14
Related Documents:
specs/_templates/WIZARD_SPEC_TEMPLATE.md - Specification template
specs/pf/specs/PF-41-configurable-module-wizards.md - PF-41 infrastructure spec
src/platform/wizards/README.md - Platform wizard implementation
docs/development/WIZARD_WORKFLOW_RECOMMENDATIONS.md - Workflow recommendations
Quick Start
1. Create Wizard Specification
# Use the wizard template
create-spec --core hr --name leave-request-wizard --type wizard
# Or let auto-detection prompt you
create-spec --core hr --name leave-request-wizard
# → "Name contains 'wizard'. Use WIZARD_SPEC_TEMPLATE.md? (Y/n)"
Output: specs/hr/ux/HR-UX-##-leave-request-wizard.md
2. Validate Specification
validate-spec --file specs/hr/ux/HR-UX-04-leave-request-wizard.md --type wizard
3. Generate Implementation Tasks
4. Implement and Test
Follow the implementation plan in the spec, using PF-41 infrastructure.
Wizard Development Lifecycle
┌─────────────────────────────────────────────────────────────────┐
│ WIZARD DEVELOPMENT LIFECYCLE │
├─────────────────────────────────────────────────────────────────┤
│ │
│ 1. SPECIFICATION 2. PLANNING 3. IMPLEMENTATION │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ Create spec │───────▶│ Choose PF-41 │────▶│ Build steps │ │
│ │ using wizard │ │ approach │ │ & components │ │
│ │ template │ │ │ │ │ │
│ └──────────────┘ └──────────────┘ └──────────────┘ │
│ │ │ │ │
│ ▼ ▼ ▼ │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ Define steps │ │ Design data │ │ Register │ │
│ │ & validation │ │ model │ │ with PF-41 │ │
│ └──────────────┘ └──────────────┘ └──────────────┘ │
│ │ │
│ ▼ │
│ 4. TESTING 5. DEPLOYMENT 6. ANALYTICS │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ Unit tests │───────▶│ Feature flag │────▶│ Track usage │ │
│ │ E2E tests │ │ System templ │ │ & completion │ │
│ │ RLS tests │ │ │ │ │ │
│ └──────────────┘ └──────────────┘ └──────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
Phase 1: Specification
Using WIZARD_SPEC_TEMPLATE.md
The wizard specification template includes these key sections:
| Section | Purpose | Required |
|---|
| Executive Summary | Quick overview of wizard | ✅ |
| Business Context | Why this wizard is needed | ✅ |
| Objectives & Success Criteria | Measurable goals | ✅ |
| Scope | What’s in/out of scope | ✅ |
| User Stories | User-facing requirements | ✅ |
| PF-41 Wizard Architecture | Integration approach | ✅ |
| Wizard Step Definitions | Detailed step specs | ✅ |
| Navigation & Flow | Flow diagram and rules | ✅ |
| Data Model | Wizard data types | ✅ |
| Integration Points | API/hook integrations | ✅ |
| UI/UX Requirements | Design requirements | ✅ |
| Testing Strategy | Test scenarios | ✅ |
| Analytics Events | Tracking events | Recommended |
| Implementation Plan | Phased tasks | ✅ |
Step Definition Requirements
Each wizard step must document:
### Step N: [Step Name]
**Purpose:** [What this step accomplishes]
**Step Type:** `form` / `custom` / `review`
**Component ID (if custom):** `{core}.{feature}.{step}`
**Fields/Components:**
| Field | Type | Required | Validation |
|-------|------|----------|------------|
| field1 | text | Yes | min 2 chars |
| field2 | select | No | - |
**Help Content:**
- **Title:** [Help title]
- **Content:** [Help text]
**Tooltip Definitions:**
| Term | Tooltip Key | Content |
|------|-------------|---------|
| term1 | `wizard_term_definition` | [Text] |
**Navigation:**
- **Can Skip:** Yes/No - [Reason]
- **Can Go Back:** Yes/No
- **Next Step:** [Next step name]
**Mobile Considerations:**
- [ ] Touch targets ≥44x44px
- [ ] Form fields stack vertically on mobile
- [ ] Native pickers for date/time on mobile
- [ ] Validation errors visible without scrolling
Step Icons
Every wizard step SHOULD have an icon to improve visual scanning and user orientation. Icons appear inside the step circle indicator in all three layout variants (horizontal, timeline, sidebar).
Two Layers of Icon Support
| Layer | Type | Where stored | Rendering |
|---|
Presentational (WizardShellStep.icon) | React component (ComponentType) | In-memory step array | Rendered directly by WizardShell |
Template JSON (WizardStep.icon) | string key (e.g. 'user', 'calendar') | pf_wizard_templates.steps JSONB | Resolved via resolveStepIcon() at runtime |
Using Icons with WizardShell (Presentational)
Pass Lucide icon components directly in the steps array:
import { WizardShell } from '@/platform/wizards';
import { User, Calendar, ClipboardCheck } from 'lucide-react';
const steps = [
{ id: 'info', title: 'Personal Info', icon: User },
{ id: 'dates', title: 'Schedule', icon: Calendar },
{ id: 'review', title: 'Review', icon: ClipboardCheck },
];
<WizardShell steps={steps} layout="timeline" /* ... */ />
Using Icons in PF-41 Templates (JSON Config)
For configurable wizard templates stored in the database, use string icon keys from the WIZARD_STEP_ICONS catalog:
// Template step definition (JSON)
{
"id": "step-1",
"title": "Employee Info",
"icon": "user",
"component_type": "form",
// ...
}
Resolve string keys to components using resolveStepIcon():
import { resolveStepIcon } from '@/platform/wizards';
const steps = template.steps.map((step) => ({
id: step.id,
title: step.title,
icon: resolveStepIcon(step.icon),
}));
Available Icon Keys
The WIZARD_STEP_ICONS catalog in @/platform/wizards provides ~50 curated icons organized by category:
| Category | Keys |
|---|
| People | user, user-plus, users, badge-check |
| Contact | mail, phone, map-pin, home |
| Dates | calendar, clock |
| Documents | file-text, file-check, clipboard, upload, image-plus |
| Finance | wallet, banknote, credit-card, circle-dollar-sign |
| Work | briefcase, building, laptop, package |
| Health | stethoscope, heart |
| Security | shield, shield-check, lock |
| Settings | settings, target, sparkles |
| Wizard lifecycle | review, complete, confirm, summary, info |
Adding new icons: Add to src/platform/wizards/utils/stepIcons.ts, update this table, and follow the Icon Guide.
Healthcare examples: examples/wizard-templates/healthcare-icon-keys.json — copy-paste PF-41 snippets for stethoscope, pill, syringe.
Unknown icon keys (fallback behavior): If a template references an icon key that isn’t in WIZARD_STEP_ICONS and doesn’t match a module code, resolveStepIcon() returns the FALLBACK_STEP_ICON (CircleHelp from Lucide) so the step indicator stays visible instead of silently collapsing to the step number. In development, a one-time console.warn per unknown key surfaces the typo to the author; the warning is stripped from production builds. Authors should still pick a real key from the catalog — the fallback is a safety net, not a sanctioned wildcard. An empty/missing icon field is treated as the intentional “show the step number” mode and does not trigger the fallback.
Icon Selection Rules
- Semantic relevance: Choose icons that represent the step’s purpose, not its position.
- Consistency: Use the same icon for the same concept across wizards (e.g.
user for personal info, calendar for scheduling).
- Review/completion steps: Use
review or complete — these are recognizable patterns.
- No icon is acceptable: Steps without an icon fall back to step number display.
- Accessibility: Step icons are decorative (step circle has
aria-label with step title); no additional aria-label needed on icons.
Icon Display Behavior
| State | What renders |
|---|
| Completed step | Checkmark (Check icon) — always overrides step icon |
| Current step with icon | Step icon in primary border circle |
| Current step without icon | Step number in primary border circle |
| Future step with icon | Step icon in muted border circle |
| Future step without icon | Step number in muted border circle |
Phase 2: PF-41 Integration Decision
Decision Tree
Is this wizard...
│
├─ A standardized process that organizations should customize?
│ └─ YES → Use System Template approach
│
├─ Highly integrated with module-specific logic?
│ └─ YES → Use Module-Specific approach
│
└─ Both complex logic AND needs customization?
└─ YES → Use Hybrid approach
Approach Comparison
| Aspect | System Template | Module-Specific | Hybrid |
|---|
| Customizable | ✅ Yes | ❌ No | ✅ Partial |
| Marketplace | ✅ Yes | ❌ No | ⚠️ Limited |
| Implementation | PF-41 templates | Custom components | Mixed |
| Maintenance | Lower | Higher | Medium |
| Best For | Onboarding, setup | Complex workflows | Enterprise |
Implementation Patterns
System Template (Recommended)
// src/cores/{core}/pages/{Wizard}Page.tsx
import { ModuleWizardRenderer } from '@/platform/wizards';
import { useActiveWizard } from '@/platform/wizards';
export function LeaveRequestWizardPage() {
const { data: template } = useActiveWizard({
module: 'hr',
wizardType: 'leave_request',
});
return (
<ModuleWizardRenderer
template={template}
onComplete={handleComplete}
onCancel={handleCancel}
/>
);
}
Module-Specific
// src/cores/{core}/wizards/{wizard}/WizardPage.tsx
import { WizardRenderer } from '@/platform/forms/components/wizard';
import { Step1, Step2, ReviewStep } from './steps';
export function LeaveRequestWizard() {
const [step, setStep] = useState(1);
const [data, setData] = useState({});
const steps = [
{ component: Step1, title: 'Leave Type' },
{ component: Step2, title: 'Dates' },
{ component: ReviewStep, title: 'Review' },
];
return (
<WizardRenderer
steps={steps}
currentStep={step}
data={data}
onStepChange={setStep}
onDataChange={setData}
/>
);
}
Hybrid
// Combine PF-41 form steps with custom components
import { ModuleWizardRenderer, registerWizardStep } from '@/platform/wizards';
import { CustomValidationStep } from './steps/CustomValidationStep';
// Register custom step
registerWizardStep({
id: 'hr.leave.validation',
module: 'hr',
name: 'Custom Validation',
component: CustomValidationStep,
});
// Template uses both form steps and custom step
Phase 3: Implementation
File Structure
src/cores/{core}/
├── wizards/
│ ├── register{Core}WizardSteps.ts # Step registration (PF-41)
│ └── {wizard-name}/
│ ├── {WizardName}Page.tsx # Wizard page component
│ ├── steps/
│ │ ├── Step1Component.tsx # Custom step components
│ │ ├── Step2Component.tsx
│ │ └── ReviewStep.tsx
│ ├── hooks/
│ │ ├── useWizardData.ts # Data fetching
│ │ └── useWizardValidation.ts # Validation logic
│ └── types.ts # TypeScript types
Custom Step Component Pattern
// src/cores/hr/wizards/leave-request/steps/LeaveTypeStep.tsx
import { WizardStepProps } from '@/platform/wizards';
import { FormField, FormItem, FormLabel } from '@/shared/ui/form';
import { useLeaveTypes } from '@/cores/hr/hooks/useLeaveTypes';
interface LeaveTypeStepProps extends WizardStepProps {
// Step-specific props
}
export function LeaveTypeStep({
data,
onDataChange,
onValidate
}: LeaveTypeStepProps) {
const { data: leaveTypes, isLoading } = useLeaveTypes();
const handleTypeSelect = (typeId: string) => {
onDataChange({ ...data, leaveTypeId: typeId });
};
return (
<div className="space-y-4">
<FormField
name="leaveType"
render={({ field }) => (
<FormItem>
<FormLabel>Select Leave Type</FormLabel>
{/* ... form control */}
</FormItem>
)}
/>
</div>
);
}
// Validation function
LeaveTypeStep.validate = (data: WizardFormData) => {
const errors: ValidationErrors = {};
if (!data.leaveTypeId) {
errors.leaveTypeId = 'Please select a leave type';
}
return errors;
};
Step Registration (PF-41)
// src/cores/hr/wizards/registerHRWizardSteps.ts
import { registerWizardStep } from '@/platform/wizards';
import { lazy } from 'react';
export function registerHRWizardSteps() {
// Leave Request Wizard Steps
registerWizardStep({
id: 'hr.leave.type-selection',
module: 'hr',
name: 'Leave Type Selection',
description: 'Select leave type and view balance',
component: lazy(() => import('./leave-request/steps/LeaveTypeStep')),
});
registerWizardStep({
id: 'hr.leave.date-selection',
module: 'hr',
name: 'Date Selection',
description: 'Select start and end dates',
component: lazy(() => import('./leave-request/steps/DateSelectionStep')),
});
// ... additional steps
}
Draft Persistence
import { useWizardDraft } from '@/platform/wizards';
function MyWizard() {
const {
draft,
saveDraft,
loadDraft,
clearDraft
} = useWizardDraft({
wizardType: 'leave_request',
userId: user.id,
});
// Auto-save on step change
const handleStepComplete = (stepData: StepData) => {
saveDraft({
currentStep: step,
data: { ...data, ...stepData },
});
};
return (/* ... */);
}
Phase 4: Testing
Required Tests
| Test Type | Location | Coverage Target |
|---|
| Unit | tests/unit/{core}/{wizard}/ | 80% |
| Integration | tests/integration/{core}/{wizard}/ | Key flows |
| E2E | tests/e2e/{core}/{wizard}/ | Happy path + errors |
| RLS | tests/rls/{core}-{wizard}/ | If new tables |
Unit Test Example
// tests/unit/hr/leave-request-wizard/LeaveTypeStep.test.tsx
import { render, screen, fireEvent } from '@testing-library/react';
import { LeaveTypeStep } from '@/cores/hr/wizards/leave-request/steps/LeaveTypeStep';
describe('LeaveTypeStep', () => {
it('validates leave type selection', () => {
const errors = LeaveTypeStep.validate({});
expect(errors.leaveTypeId).toBe('Please select a leave type');
});
it('passes validation with leave type selected', () => {
const errors = LeaveTypeStep.validate({ leaveTypeId: 'vacation' });
expect(errors).toEqual({});
});
it('shows available leave balances', async () => {
render(<LeaveTypeStep data={{}} onDataChange={jest.fn()} />);
expect(await screen.findByText('Vacation')).toBeInTheDocument();
expect(await screen.findByText('16 hours available')).toBeInTheDocument();
});
});
E2E Test Example
// tests/e2e/hr/leave-request-wizard.spec.ts
import { test, expect } from '@playwright/test';
test.describe('Leave Request Wizard', () => {
test('completes full wizard flow', async ({ page }) => {
await page.goto('/hr/leave/request');
// Step 1: Select leave type
await page.click('[data-testid="leave-type-vacation"]');
await page.click('[data-testid="next-button"]');
// Step 2: Select dates
await page.fill('[data-testid="start-date"]', '2026-02-15');
await page.fill('[data-testid="end-date"]', '2026-02-20');
await page.click('[data-testid="next-button"]');
// Step 3: Add details
await page.fill('[data-testid="notes"]', 'Family vacation');
await page.click('[data-testid="next-button"]');
// Step 4: Review and submit
await expect(page.locator('[data-testid="summary-leave-type"]'))
.toHaveText('Vacation');
await page.click('[data-testid="submit-button"]');
// Verify success
await expect(page.locator('[data-testid="success-message"]'))
.toBeVisible();
});
test('saves and resumes draft', async ({ page }) => {
await page.goto('/hr/leave/request');
// Complete step 1
await page.click('[data-testid="leave-type-vacation"]');
await page.click('[data-testid="next-button"]');
// Navigate away
await page.goto('/hr/dashboard');
// Return to wizard
await page.goto('/hr/leave/request');
// Should resume at step 2
await expect(page.locator('[data-testid="step-indicator"]'))
.toHaveText('Step 2 of 4');
});
});
Phase 5: Analytics Integration
Standard Events
All wizards should track these events:
import { trackWizardEvent } from '@/platform/wizards';
// Wizard started
trackWizardEvent('leave_request_wizard_started', {
wizard_id: templateId,
user_id: userId,
device_type: isMobile ? 'mobile' : 'desktop',
entry_point: 'dashboard_quick_action',
});
// Step completed
trackWizardEvent('leave_request_wizard_step_completed', {
step_id: 'date-selection',
step_name: 'Date Selection',
time_spent_ms: 45000,
fields_completed: 3,
});
// Wizard completed
trackWizardEvent('leave_request_wizard_completed', {
total_time_ms: 120000,
steps_completed: 4,
steps_skipped: 0,
entity_id: leaveRequestId,
});
PF-41 Built-in Analytics
When using PF-41 ModuleWizardRenderer, analytics are tracked automatically:
<ModuleWizardRenderer
template={template}
trackAnalytics={true} // Default: true
onComplete={handleComplete}
/>
Phase 6: Deployment
Feature Flags
Use feature flags for gradual rollout:
import { useWizardFeatureFlags } from '@/platform/wizards';
function LeaveRequestPage() {
const { useConfigurableWizard } = useWizardFeatureFlags('hr');
if (useConfigurableWizard) {
return <ConfigurableLeaveWizard />;
}
return <LegacyLeaveForm />;
}
System Template Deployment
For PF-41 system templates, create a migration:
-- supabase/migrations/YYYYMMDDHHMMSS_add_leave_request_wizard_template.sql
INSERT INTO pf_wizard_templates (
module,
wizard_type,
name,
description,
wizard_config,
steps,
is_template,
is_active
) VALUES (
'hr',
'leave_request',
'Leave Request Wizard',
'Guided workflow for requesting time off',
'{"progress_style": "stepper", "validation_mode": "on_advance"}',
'[{"id": "step-1", "title": "Leave Type", "component_type": "custom", "component_id": "hr.leave.type-selection"}, ...]'::jsonb,
true, -- System template
true -- Active
);
Common Pitfalls
1. Missing PF-41 Integration Documentation
Problem: Spec doesn’t document PF-41 approach.
Solution: Always include the “PF-41 Wizard Architecture” section, even for module-specific wizards:
## PF-41 Wizard Architecture
**Approach:** Module-Specific
**Rationale:** This wizard requires complex validation logic that integrates
deeply with HR leave policy rules. A future enhancement will migrate to
PF-41 hybrid approach for organization customization.
2. Incomplete Step Definitions
Problem: Steps missing validation rules, help content, or mobile considerations.
Solution: Use the complete step template and run validate-spec --file <path> --type wizard to catch gaps.
3. No Draft Persistence
Problem: Users lose progress when navigating away.
Solution: Always implement draft persistence using useWizardDraft or PF-41’s built-in draft management.
4. Missing Analytics
Problem: No tracking of wizard usage and drop-off points.
Solution: Include the Analytics Events section in spec and implement tracking.
5. Poor Mobile Experience
Problem: Wizard unusable on mobile devices.
Solution:
- Use the mobile checklist per step
- Test on actual mobile devices
- Include mobile E2E tests
Commands Reference
| Command | Purpose |
|---|
create-wizard-spec | Interactive wizard UX spec creation (step prompts, icon selection, PF-41 decisions) |
create-spec --type wizard | Quick wizard spec creation from template |
validate-spec --type wizard | Validate wizard spec (icons, naming, validation pattern, PF-41, sections) |
audit-wizard-specs | Audit all wizard specs across cores with scoring (icons, naming, compliance) |
recommend-wizards | Analyze module specs for wizard candidates with scoring |
generate-wizard-lovable-prompts | Generate Lovable browser walkthrough prompts from wizard specs |
generate-tasks | Generate implementation tasks from spec |
verify-task | Verify task completion |
Full workflow:
recommend-wizards --core {core}
↓
create-wizard-spec --core {core} --name {name} --based-on {SPEC-ID}
↓
validate-spec --file specs/{core}/ux/{CORE}-UX-##-{name}.md --type wizard
↓
generate-tasks {CORE}-UX-##
↓
implement (using WizardShell / DialogWizardShell / ModuleWizardRenderer)
↓
audit-wizard-specs --core {core}
Resources
- Specification Template:
specs/_templates/WIZARD_SPEC_TEMPLATE.md
- PF-41 Spec:
specs/pf/specs/PF-41-configurable-module-wizards.md
- Platform Implementation:
src/platform/wizards/README.md
- Consolidation Plan:
specs/cross-cutting/WIZARD-CONSOLIDATION-PLAN.md
- Cross-Cutting Patterns:
specs/cross-cutting/WIZARD-PATTERN.md
- Cross-Cutting Rules:
specs/cross-cutting/WIZARD-RULES.md
- Selection Guide:
docs/guides/use-cases/wizard-selection.md
- UX Standard:
docs/development/WIZARD_UX_STANDARD.md
- Icon Guide:
docs/development/ICON_GUIDE.md
- Workflow Recommendations:
docs/development/WIZARD_WORKFLOW_RECOMMENDATIONS.md
- Analytics Guide:
docs/pf/wizard-analytics-guide.md
- Branching Guide:
docs/pf/wizard-branching-guide.md
- Marketplace Guide:
docs/pf/wizard-marketplace-guide.md
Architecture Recommendations
Wizard Infrastructure Audit Findings (2026-04-14)
The platform currently has three distinct wizard rendering paths. This section documents known gaps, recommended consolidations, and standards to enforce going forward.
1. Consolidate Step Registration at Bootstrap
Wizard step registration should happen centrally in src/bootstrap/registerModuleWizardSteps.ts so module entry points do not race dynamic step availability.
Current baseline (recommended pattern): register all core step modules in registerAllModuleWizardSteps():
// src/bootstrap/registerModuleWizardSteps.ts
const WIZARD_MODULES = [
{ id: 'fa.wizards', load: () => import('@/cores/fa/wizards/registerFAWizardSteps') },
{ id: 'ce.screening', load: () => import('@/cores/ce/wizards/screening/registerScreeningWizardSteps') },
{ id: 'ce.partner-contract', load: () => import('@/cores/ce/wizards/partner-contract/registerPartnerContractWizardSteps') },
{ id: 'ce.sequences', load: () => import('@/cores/ce/wizards/sequences/registerCeSequenceWizardSteps') },
{ id: 'gr.wizards', load: () => import('@/cores/gr/wizards/registerGRWizardSteps') },
{ id: 'gr.training-course', load: () => import('@/cores/gr/wizards/training-course/registerTrainingCourseWizardSteps') },
{ id: 'hr.wizards', load: () => import('@/cores/hr/components/wizards/registerHRWizardSteps') },
{ id: 'hr.payroll-wizards', load: () => import('@/cores/hr/wizards/registerHRPayrollWizardSteps') },
{ id: 'rh.wizards', load: () => import('@/cores/rh/components/wizards/registerRHWizardSteps') },
] as const;
export function registerAllModuleWizardSteps(): void {
for (const moduleSpec of WIZARD_MODULES) {
// Load each module independently so one failure cannot block app boot.
void moduleSpec.load();
}
}
2. Deprecate Custom Stepper UIs
Custom stepper usage should be triaged as either (a) migration target or (b) documented exception:
| Custom stepper | Location | Recommendation |
|---|
PayrollWizardStepper | src/cores/hr/components/payroll/ | Deprecated / removed (migrated to PF-41 renderer flows) |
KioskProgressStepper | src/cores/pm/components/kiosk/ | Migrated to WizardShell horizontal header |
MigrationStepper | src/platform/settings/pages/data-migration/ | Exception: tab/segmented navigation, not a sequential wizard |
StepIndicator (inline) | src/cores/pm/components/PreadmissionFormRenderer.tsx | Exception: schema-driven section renderer, not PF-41 wizard navigation |
ApprovalChainStepper | src/cores/fw/components/approvals/ApprovalChainStepper.tsx | Exception: approval-state tracker (read-only process visualization), not step navigation |
PDSACycleDetail phase row | src/cores/gr/components/PDSACycleDetail.tsx | Exception: business process phase viewer (Plan/Do/Study/Act), not a wizard |
StepIndicator (inline) | src/platform/organizations/OnboardingDialog.tsx | Migrated to DialogWizardShell |
New wizard builds MUST NOT create custom steppers. Allowed exceptions are limited to non-wizard patterns (parallel tabs, schema-section progress) and should be explicitly documented in spec/guide artifacts.
3. Icon String Resolution for Template Wizards
The WizardStep.icon field (JSON config) accepts a string, while WizardShellStep.icon (presentational) accepts a React component. The resolveStepIcon() utility bridges this gap. ModuleWizardRenderer and any code converting template steps to shell steps SHOULD use resolveStepIcon() to map string keys to components.
4. Standardize Wizard Validation Patterns
Three validation patterns exist across wizard implementations:
| Pattern | Used by | Recommendation |
|---|
PF-41 validation.rules on WizardStep | Template-driven ModuleWizardRenderer flows | Preferred for form steps |
StepComponentProps-driven validation | Custom registerWizardStep components | Preferred for new custom steps (static .validate() is legacy-compatible only) |
Inline react-hook-form validation | Ad-hoc WizardShell / DialogWizardShell flows | Acceptable for shell-only implementations |
Avoid mixing patterns within the same wizard. Template-driven wizards should use PF-41 validation exclusively.
5. Wizard Selection Decision Tree
Need a multi-step flow?
│
├─ Parallel tab navigation (non-sequential)?
│ └─ Not a wizard — use tabs / segmented controls
│
├─ Form data capture only (no business logic)?
│ └─ Use FW-31 MultiPageFormRenderer
│
├─ Guided business process with custom logic?
│ │
│ ├─ Organizations should customize steps?
│ │ └─ PF-41 System Template + ModuleWizardRenderer
│ │
│ ├─ Tightly coupled to module internals?
│ │ └─ WizardShell + custom step components
│ │
│ └─ Both customizable AND complex?
│ └─ PF-41 Hybrid (form + custom steps via registerWizardStep)
│
└─ Short dialog-based flow (≤4 steps)?
└─ DialogWizardShell
6. Standard Step Naming Conventions
Steps across all wizards SHOULD follow these naming patterns for consistency:
| Step purpose | Recommended title | Icon key |
|---|
| Personal/demographic data | ”Personal Information” | user |
| Contact details | ”Contact Information” | mail |
| Employment/role details | ”Employment Details” | briefcase |
| Address/location | ”Address” | map-pin |
| Schedule/dates | ”Schedule” | calendar |
| Financial/payment | ”Payment Information” | wallet |
| Document upload | ”Documents” | file-text |
| Credentials/compliance | ”Credentials” | shield-check |
| Consent/agreements | ”Consent & Agreements” | clipboard-check |
| Settings/config | ”Configuration” | settings |
| Summary/review | ”Review & Submit” | review |
| Success/completion | ”Complete” | complete |
Last Updated: 2026-05-13