wizzard-stepper-react 1.5.1 โ†’ 1.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,370 +1,431 @@
1
- # wizzard-stepper-react ๐Ÿง™โ€โ™‚๏ธ
2
-
3
- A flexible, headless, and strictly typed multi-step wizard library for React. Built with adapter patterns in mind to support any form library (React Hook Form, Formik, etc.) and any validation schema (Zod, Yup).
4
-
5
- ## Features
6
-
7
- - ๐Ÿง  **Headless Architecture**: Full control over UI. You bring the components, we provide the logic.
8
- - ๐Ÿ”Œ **Adapter Pattern**: Zero-dependency adapters for **Zod**, **Yup** validation. No hard dependencies on these libraries in the core.
9
- - ๐Ÿ—๏ธ **Complex Data**: Built-in support for nested objects and arrays using dot notation (`users[0].name`).
10
- - ๐Ÿ›ก๏ธ **Strictly Typed**: Built with TypeScript generics for type safety across steps.
11
- - ๐Ÿ”€ **Conditional Steps**: Dynamic pipelines where steps can be skipped based on data.
12
- - ๐Ÿ’พ **Persistence**: Auto-save progress to LocalStorage or custom stores.
13
- - โšก **Auto Validation**: Block navigation until the step is valid.
14
-
15
- ## Installation
16
-
17
- ```bash
18
- npm install wizzard-stepper-react
19
- # or
20
- yarn add wizzard-stepper-react
21
- # or
22
- pnpm add wizzard-stepper-react
23
- ```
24
-
25
- ## Usage
26
-
27
- ### 1. Basic Usage (Compatible & Simple)
28
-
29
- The quickest way to get started. Types are flexible (`any`).
30
-
31
- ```tsx
32
- import { WizardProvider, useWizard } from 'wizzard-stepper-react';
33
-
34
- const Step1 = () => {
35
- const { wizardData, handleStepChange } = useWizard();
36
- return (
37
- <input
38
- value={wizardData.name}
39
- onChange={(e) => handleStepChange('name', e.target.value)}
40
- />
41
- );
42
- };
43
-
44
- const App = () => (
45
- <WizardProvider>
46
- <Step1 />
47
- </WizardProvider>
48
- );
49
- ```
50
-
51
- ### 2. Strict Usage (Factory Pattern ๐Ÿญ)
52
-
53
- For production apps, use the factory pattern to get perfect type inference.
54
-
55
- **`wizards/my-wizard.ts`**
56
- ```typescript
57
- import { createWizardFactory } from 'wizzard-stepper-react';
58
-
59
- interface MySchema {
60
- name: string;
61
- age: number;
62
- }
63
-
64
- export const { WizardProvider, useWizard } = createWizardFactory<MySchema>();
65
- ```
66
-
67
- **`components/MyForm.tsx`**
68
- ```tsx
69
- import { useWizard } from '../wizards/my-wizard';
70
-
71
- const Step1 = () => {
72
- const { wizardData } = useWizard();
73
- // โœ… wizardData is strictly typed as MySchema
74
- // โœ… Autocomplete works for wizardData.name
75
- }
76
- ```
77
-
78
- See [MIGRATION.md](./MIGRATION.md) for details on switching to strict mode.
79
-
80
- ## Integration with React Hook Form + Zod
81
-
82
- ```tsx
83
- import { useForm } from 'react-hook-form';
84
- import { zodResolver } from '@hookform/resolvers/zod';
85
- import { z } from 'zod';
86
- import { ZodAdapter, useWizard } from 'wizzard-stepper-react';
87
-
88
- const schema = z.object({ email: z.string().email() });
89
-
90
- const MyStep = () => {
91
- const { handleStepChange, wizardData } = useWizard();
92
- const { register } = useForm({
93
- defaultValues: wizardData,
94
- resolver: zodResolver(schema),
95
- mode: 'onChange' // Important: validate real-time or bind changes
96
- });
97
-
98
- return (
99
- <input {...register('email', {
100
- onChange: (e) => handleStepChange('email', e.target.value)
101
- })} />
102
- );
103
- }
104
-
105
- // In Config:
106
- const config = {
107
- steps: [
108
- {
109
- id: 'step1',
110
- label: 'Email',
111
- // Zero-dependency: works with any Zod version
112
- validationAdapter: new ZodAdapter(schema)
113
- }
114
- ]
115
- }
116
- ```
117
-
118
- ## Complex Data (Arrays & Objects)
119
-
120
- The library provides `setData` and `getData` helpers that support deep paths using dot notation and array brackets.
121
-
122
- ```tsx
123
- const { setData, wizardData } = useWizard<MyData>();
124
-
125
- // Set nested object property
126
- setData('user.profile.name', 'John');
127
-
128
- // Set array item property
129
- setData('items[0].value', 'New Value');
130
-
131
- // Get with default value
132
- const name = getData('user.profile.name', 'Anonymous');
133
-
134
- // ๐Ÿ†• Bulk Update (Autofill)
135
- const autoFillParams = () => {
136
- // Merges into existing data
137
- updateData({
138
- name: 'John Doe',
139
- email: 'john@example.com'
140
- });
141
- };
142
- ```
143
-
144
- ## Performance & Optimization ๐Ÿš€
145
-
146
- For large forms (e.g., 50+ array items), using `useWizard` context can cause performance issues because it triggers a re-render on every keystroke. To solve this, we provide **granular hooks** that allow components to subscribe only to the specific data they need.
147
-
148
- ### 1. Use `useWizardValue` for Granular Updates
149
-
150
- Instead of reading the whole `wizardData`, subscribe to a single field. The component will only re-render when *that specific field* changes.
151
-
152
- ```tsx
153
- // โœ… FAST: Only re-renders when "users[0].name" changes
154
- const NameInput = () => {
155
- // Subscribe to specific path
156
- const name = useWizardValue('users[0].name');
157
- const { setData } = useWizardActions(); // Component actions don't trigger re-renders
158
-
159
- return <input value={name} onChange={e => setData('users[0].name', e.target.value)} />;
160
- }
161
-
162
- // โŒ SLOW: Re-renders on ANY change in the form
163
- const NameInputSlow = () => {
164
- const { wizardData, setData } = useWizard();
165
- return <input value={wizardData.users[0].name} ... />;
166
- }
167
- ```
168
-
169
- ### 2. Use `useWizardSelector` for Lists
170
-
171
- When rendering lists, avoid passing the whole `children` array to the parent component. Instead, select only IDs and let child components fetch their own data.
172
-
173
- ```tsx
174
- const ChildrenList = () => {
175
- // โœ… Only re-renders when the list LENGTH changes or IDs change
176
- const childIds = useWizardSelector(state => state.children.map(c => c.id));
177
-
178
- return (
179
- <div>
180
- {childIds.map((id, index) => (
181
- // Pass ID/Index, NOT the data object
182
- <ChildRow key={id} index={index} />
183
- ))}
184
- </div>
185
- );
186
- }
187
- ```
188
-
189
- ### 3. Debounced Validation
190
-
191
- For heavy validation schemas, you can debounce validation to keep the UI responsive.
192
-
193
- ```tsx
194
- setData('field.path', value, {
195
- debounceValidation: 300 // Wait 300ms before running Zod/Yup validation
196
- });
197
- ```
198
-
199
- ## Conditional Steps
200
-
201
- Steps can be dynamically included based on the wizard's state.
202
-
203
- ```tsx
204
- const config: IWizardConfig = {
205
- steps: [
206
- { id: 'start', label: 'Start' },
207
- {
208
- id: 'bonus',
209
- label: 'Bonus Step',
210
- // Only show if 'wantBonus' is true
211
- condition: (data) => !!data.wantBonus
212
- }
213
- ]
214
- }
215
- ```
216
-
217
- ## Persistence
218
-
219
- Save progress automatically to LocalStorage to survive page reloads.
220
-
221
- ```tsx
222
- import { LocalStorageAdapter } from 'wizzard-stepper-react';
223
-
224
- const config: IWizardConfig = {
225
- persistence: {
226
- mode: 'onChange', // Save on every keystroke
227
- adapter: new LocalStorageAdapter('my_wizard_prefix_')
228
- },
229
- steps: [...]
230
- }
231
- ```
232
-
233
- ## Advanced Features ๐ŸŒŸ
234
-
235
- ### 1. Step Renderer (Declarative UI)
236
-
237
- Instead of manual switch statements with `currentStep.id`, trust the renderer!
238
-
239
- ```typescript
240
- // Define component in config
241
- const steps = [
242
- { id: 'step1', label: 'Start', component: Step1Component },
243
- { id: 'step2', label: 'End', component: Step2Component },
244
- ];
245
-
246
- // Render
247
- const App = () => (
248
- <WizardProvider config={{ steps }}>
249
- <WizardStepRenderer
250
- wrapper={({ children }) => (
251
- <motion.div initial={{ opacity: 0 }} animate={{ opacity: 1 }}>
252
- {children}
253
- </motion.div>
254
- )}
255
- />
256
- </WizardProvider>
257
- );
258
- ```
259
-
260
- ### 2. Routing Integration
261
-
262
- Sync wizard state with URL using `onStepChange`.
263
-
264
- ```tsx
265
- const navigate = useNavigate();
266
-
267
- const config: IWizardConfig = {
268
- // 1. Sync State -> URL
269
- onStepChange: (prev, next, data) => {
270
- navigate(`/wizard/${next}`);
271
- // Optional: Send event to Analytics
272
- trackEvent('wizard_step', { step: next });
273
- },
274
- steps: [...]
275
- };
276
-
277
- // 2. Sync URL -> State (Initial Load)
278
- const { stepId } = useParams();
279
-
280
- return <WizardProvider config={config} initialStepId={stepId}>...</WizardProvider>;
281
- ```
282
-
283
- ### 3. Granular Persistence
284
-
285
- By default, the wizard uses `MemoryAdapter` (RAM only). You can enable `LocalStorage` globally, but override it for sensitive steps.
286
-
287
- ```tsx
288
- const config: IWizardConfig = {
289
- // Global: Persist everything to LocalStorage
290
- persistence: { adapter: new LocalStorageAdapter('wizard_') },
291
- steps: [
292
- {
293
- id: 'public',
294
- label: 'Public Info',
295
- // Inherits global adapter (LocalStorage)
296
- },
297
- {
298
- id: 'sensitive',
299
- label: 'Credit Card',
300
- // Override: Store strictly in memory (cleared on refresh)
301
- persistenceAdapter: new MemoryAdapter()
302
- }
303
- ]
304
- };
305
- ```
306
-
307
- ## API Reference
308
-
309
- ### `IWizardConfig<T>`
310
-
311
- - `steps`: Array of step configurations.
312
- - `persistence`: Configuration for state persistence.
313
- - `autoValidate`: (obj) Global validation setting.
314
-
315
- ### `useWizard<T>()`
316
-
317
- - `activeSteps`: Steps that match conditions.
318
- - `currentStep`: The currently active step object.
319
- - `wizardData`: The global state object (subscribe cautiously!).
320
- - `setData(path, value, options?)`: Update state. Options: `{ debounceValidation: number }`.
321
- - `getData(path, defaultValue?)`: Retrieve nested data.
322
- - `handleStepChange(key, value)`: simple helper to update top-level state.
323
- - `goToNextStep()`: Validates and moves next.
324
- - `goToStep(id)`: Jumps to specific step.
325
- - `allErrors`: Map of validation errors.
326
-
327
- ### New Performance Hooks
328
-
329
- #### `useWizardValue<T>(path: string)`
330
-
331
- Subscribes to a specific data path. Re-renders **only** when that value changes.
332
-
333
- #### `useWizardError(path: string)`
334
-
335
- Subscribes to validation errors for a specific path. Highly recommended for individual inputs.
336
-
337
- #### `useWizardSelector<T>(selector: (state: T) => any)`
338
-
339
- Create a custom subscription to the wizard state. Ideal for derived state or lists.
340
-
341
- #### `useWizardActions()`
342
-
343
- Returns object with actions (`setData`, `goToNextStep`, etc.) **without** subscribing to state changes. Use this in components that trigger updates but don't need to render data.
344
-
345
- ## Demos
346
-
347
- Check out the [Live Demo](https://ZizzX.github.io/wizzard-stepper-react/), [NPM](https://www.npmjs.com/package/wizzard-stepper-react) or the [source code](https://github.com/ZizzX/wizzard-stepper-react-demo) for a complete implementation featuring:
348
-
349
- - **Tailwind CSS v4** UI overhaul.
350
- - **React Hook Form + Zod** integration.
351
- - **Formik + Yup** integration.
352
- - **Conditional Routing** logic.
353
- - **Advanced Features Demo**: (`/advanced`) showcasing:
354
- - **Autofill**: `updateData` global merge.
355
- - **Declarative Rendering**: `<WizardStepRenderer />`.
356
- - **Granular Persistence**: Hybrid Memory/LocalStorage.
357
-
358
- ## Advanced Demo Guide ๐Ÿงช
359
-
360
- Visit `/advanced` in the demo to try:
361
-
362
- 1. **Autofill**: Click "Magic Autofill" to test `updateData()`. It instantly populates the form (merged with existing data).
363
- 2. **Hybrid Persistence**:
364
- * **Step 1 (Identity)**: Refreshes persist (LocalStorage).
365
- * **Step 2 (Security)**: Refreshes CLEAR data (MemoryAdapter).
366
- 3. **Declarative UI**: The steps are rendered using `<WizardStepRenderer />` with Framer Motion animations, defined in the config!
367
-
368
- ## License
369
-
370
- MIT
1
+ # wizzard-stepper-react ๐Ÿง™โ€โ™‚๏ธ
2
+
3
+ A flexible, headless, and strictly typed multi-step wizard library for React. Built with adapter patterns in mind to support any form library (React Hook Form, Formik, etc.) and any validation schema (Zod, Yup).
4
+
5
+ ## Features
6
+
7
+ - ๐Ÿง  **Headless Architecture**: Full control over UI. You bring the components, we provide the logic.
8
+ - ๐Ÿ”Œ **Adapter Pattern**: Zero-dependency adapters for **Zod**, **Yup** validation.
9
+ - ๐Ÿ—๏ธ **Complex Data**: Built-in support for nested objects and arrays using dot notation (`users[0].name`).
10
+ - ๐Ÿ›ก๏ธ **Strictly Typed**: Built with TypeScript generics for type safety across steps.
11
+ - ๐Ÿ’พ **Advanced Persistence**: Auto-save progress to LocalStorage, custom stores, or **Hybrid Step-Level persistence**.
12
+ - ๏ฟฝ **Comprehensive Guides**: Detailed documentation portal with interactive guides, pro-tips, and type references.
13
+ - โšก **High Performance Engine**: Path caching, Hash-Map lookups, and Stateless Provider architecture.
14
+
15
+ ## Installation
16
+
17
+ ```bash
18
+ npm install wizzard-stepper-react
19
+ # or
20
+ yarn add wizzard-stepper-react
21
+ # or
22
+ pnpm add wizzard-stepper-react
23
+ ```
24
+
25
+ ## Usage
26
+
27
+ ### 1. Basic Usage (Compatible & Simple)
28
+
29
+ The quickest way to get started. Types are flexible (`any`).
30
+
31
+ ```tsx
32
+ import { WizardProvider, useWizard } from 'wizzard-stepper-react';
33
+
34
+ const Step1 = () => {
35
+ const { wizardData, handleStepChange } = useWizard();
36
+ return (
37
+ <input
38
+ value={wizardData.name}
39
+ onChange={(e) => handleStepChange('name', e.target.value)}
40
+ />
41
+ );
42
+ };
43
+
44
+ const App = () => (
45
+ <WizardProvider>
46
+ <Step1 />
47
+ </WizardProvider>
48
+ );
49
+ ```
50
+
51
+ ### 2. Strict Usage (Factory Pattern ๐Ÿญ)
52
+
53
+ For production apps, use the factory pattern to get perfect type inference.
54
+
55
+ **`wizards/my-wizard.ts`**
56
+ ```typescript
57
+ import { createWizardFactory } from 'wizzard-stepper-react';
58
+
59
+ interface MySchema {
60
+ name: string;
61
+ age: number;
62
+ }
63
+
64
+ export const { WizardProvider, useWizard } = createWizardFactory<MySchema>();
65
+ ```
66
+
67
+ **`components/MyForm.tsx`**
68
+ ```tsx
69
+ import { useWizard } from '../wizards/my-wizard';
70
+
71
+ const Step1 = () => {
72
+ const { wizardData } = useWizard();
73
+ // โœ… wizardData is strictly typed as MySchema
74
+ // โœ… Autocomplete works for wizardData.name
75
+ }
76
+ ```
77
+
78
+ See [MIGRATION.md](./MIGRATION.md) for details on switching to strict mode.
79
+
80
+ ## Integration with React Hook Form + Zod
81
+
82
+ ```tsx
83
+ import { useForm } from 'react-hook-form';
84
+ import { zodResolver } from '@hookform/resolvers/zod';
85
+ import { z } from 'zod';
86
+ import { ZodAdapter, useWizard } from 'wizzard-stepper-react';
87
+
88
+ const schema = z.object({ email: z.string().email() });
89
+
90
+ const MyStep = () => {
91
+ const { handleStepChange, wizardData } = useWizard();
92
+ const { register } = useForm({
93
+ defaultValues: wizardData,
94
+ resolver: zodResolver(schema),
95
+ mode: 'onChange' // Important: validate real-time or bind changes
96
+ });
97
+
98
+ return (
99
+ <input {...register('email', {
100
+ onChange: (e) => handleStepChange('email', e.target.value)
101
+ })} />
102
+ );
103
+ }
104
+
105
+ // In Config:
106
+ const config = {
107
+ steps: [
108
+ {
109
+ id: 'step1',
110
+ label: 'Email',
111
+ // Zero-dependency: works with any Zod version
112
+ validationAdapter: new ZodAdapter(schema)
113
+ }
114
+ ]
115
+ }
116
+ ```
117
+
118
+ ## Complex Data (Arrays & Objects)
119
+
120
+ The library provides `setData` and `getData` helpers that support deep paths using dot notation and array brackets.
121
+
122
+ ```tsx
123
+ const { setData, wizardData } = useWizard<MyData>();
124
+
125
+ // Set nested object property
126
+ setData('user.profile.name', 'John');
127
+
128
+ // Set array item property
129
+ setData('items[0].value', 'New Value');
130
+
131
+ // Get with default value
132
+ const name = getData('user.profile.name', 'Anonymous');
133
+
134
+ // ๐Ÿ†• Bulk Update (Autofill)
135
+ const autoFillParams = () => {
136
+ // Merges into existing data
137
+ updateData({
138
+ name: 'John Doe',
139
+ email: 'john@example.com'
140
+ });
141
+ };
142
+ ```
143
+
144
+ ## Performance & Optimization ๐Ÿš€
145
+
146
+ **New in v2**: The library now uses **path caching** and **iterative updates** under the hood. `setData` is incredibly fast even for deeply nested objects.
147
+ For large forms (e.g., 50+ array items), using `useWizard` context can still cause React re-renders. To solve this, we provide granular hooks:
148
+
149
+ ### 1. Use `useWizardValue` for Granular Updates
150
+
151
+ Instead of reading the whole `wizardData`, subscribe to a single field. The component will only re-render when *that specific field* changes.
152
+
153
+ ```tsx
154
+ // โœ… FAST: Only re-renders when "users[0].name" changes
155
+ const NameInput = () => {
156
+ // Subscribe to specific path
157
+ const name = useWizardValue('users[0].name');
158
+ const { setData } = useWizardActions(); // Component actions don't trigger re-renders
159
+
160
+ return <input value={name} onChange={e => setData('users[0].name', e.target.value)} />;
161
+ }
162
+
163
+ // โŒ SLOW: Re-renders on ANY change in the form
164
+ const NameInputSlow = () => {
165
+ const { wizardData, setData } = useWizard();
166
+ return <input value={wizardData.users[0].name} ... />;
167
+ }
168
+ ```
169
+
170
+ ### 2. Use `useWizardSelector` for Lists
171
+
172
+ When rendering lists, avoid passing the whole `children` array to the parent component. Instead, select only IDs and let child components fetch their own data.
173
+
174
+ ```tsx
175
+ const ChildrenList = () => {
176
+ // โœ… Only re-renders when the list LENGTH changes or IDs change
177
+ const childIds = useWizardSelector(state => state.children.map(c => c.id));
178
+
179
+ return (
180
+ <div>
181
+ {childIds.map((id, index) => (
182
+ // Pass ID/Index, NOT the data object
183
+ <ChildRow key={id} index={index} />
184
+ ))}
185
+ </div>
186
+ );
187
+ }
188
+ ```
189
+
190
+ ### 3. Debounced Validation
191
+
192
+ For heavy validation schemas, you can debounce validation to keep the UI responsive.
193
+
194
+ ```tsx
195
+ setData('field.path', value, {
196
+ debounceValidation: 300 // Wait 300ms before running Zod/Yup validation
197
+ });
198
+ ```
199
+
200
+ ## Validation Logic ๐Ÿ›ก๏ธ
201
+
202
+ By default, validation runs on every change (`onChange`). You can optimize this for heavy forms.
203
+
204
+ ### โšก Performance & Validation
205
+ ### granular Validation Control
206
+ - ๐Ÿงฉ **Flexible adapters** for any validation or persistence logic
207
+ - โšก **High Performance**: Stateless Provider architecture and O(1) internal lookups
208
+ - ๐Ÿ“ฆ **Zero Dependencies** (excluding React as peer)
209
+ You can control when validation happens using `validationMode`. This is critical for performance in large forms or heavy validation schemas.
210
+
211
+ - **`onChange`** (Default): Validates fields as you type (debounced). Best for immediate feedback.
212
+ - **`onStepChange`**: Validates ONLY when trying to move to the next step.
213
+ - *Ux improvement*: If an error occurs, it appears. But as soon as the user starts typing to fix it, the error is **immediately cleared** locally (without triggering a full re-validation schema check), providing instant "clean" feedback.
214
+ - **`manual`**: No automatic validation. You manually call `validateStep()`.
215
+
216
+ ```typescript
217
+ const config: IWizardConfig = {
218
+ steps: [
219
+ {
220
+ id: 'heavy-step',
221
+ label: 'Complex Data',
222
+ // Optimize: Only validate on "Next" click
223
+ validationMode: 'onStepChange',
224
+ validationAdapter: new ZodAdapter(heavySchema)
225
+ }
226
+ ]
227
+ };
228
+ ```
229
+
230
+ ### Internal Optimizations
231
+ - **Hash Table Storage**: Errors are stored internally using `Map` (Hash Table) for **O(1)** access and deletion, ensuring UI stays snappy even with hundreds of errors.
232
+ - **Path Caching**: Data access paths (e.g., `users[0].name`) are cached to eliminate parsing overhead during frequent typing.
233
+
234
+ ## Conditional Steps
235
+
236
+ Steps can be dynamically included based on the wizard's state.
237
+
238
+ ```tsx
239
+ const config: IWizardConfig = {
240
+ steps: [
241
+ { id: 'start', label: 'Start' },
242
+ {
243
+ id: 'bonus',
244
+ label: 'Bonus Step',
245
+ // Only show if 'wantBonus' is true
246
+ condition: (data) => !!data.wantBonus
247
+ }
248
+ ]
249
+ }
250
+ ```
251
+
252
+ ## ๐Ÿ’พ Persistence
253
+ Automatically save wizard progress so users don't lose data on reload.
254
+
255
+ ```typescript
256
+ import { LocalStorageAdapter } from 'wizzard-stepper-react';
257
+
258
+ const config: IWizardConfig = {
259
+ steps: [...],
260
+ persistence: {
261
+ adapter: new LocalStorageAdapter('my-wizard-key'),
262
+ mode: 'onStepChange' // or 'onChange' (heavier)
263
+ }
264
+ };
265
+ ```
266
+ Everything (current step, data, visited state) is handled automatically! LocalStorage to survive page reloads.
267
+
268
+
269
+
270
+ ## Advanced Features ๐ŸŒŸ
271
+
272
+ ### 1. Step Renderer (Declarative UI)
273
+
274
+ Instead of manual switch statements with `currentStep.id`, trust the renderer!
275
+
276
+ ```typescript
277
+ // Define component in config
278
+ const steps = [
279
+ { id: 'step1', label: 'Start', component: Step1Component },
280
+ { id: 'step2', label: 'End', component: Step2Component },
281
+ ];
282
+
283
+ // Render
284
+ const App = () => (
285
+ <WizardProvider config={{ steps }}>
286
+ <WizardStepRenderer
287
+ wrapper={({ children }) => (
288
+ <motion.div initial={{ opacity: 0 }} animate={{ opacity: 1 }}>
289
+ {children}
290
+ </motion.div>
291
+ )}
292
+ />
293
+ </WizardProvider>
294
+ );
295
+ ```
296
+
297
+ ### 2. Routing Integration
298
+
299
+ Sync wizard state with URL using `onStepChange`.
300
+
301
+ ```tsx
302
+ const navigate = useNavigate();
303
+
304
+ const config: IWizardConfig = {
305
+ // 1. Sync State -> URL
306
+ onStepChange: (prev, next, data) => {
307
+ navigate(`/wizard/${next}`);
308
+ // Optional: Send event to Analytics
309
+ trackEvent('wizard_step', { step: next });
310
+ },
311
+ steps: [...]
312
+ };
313
+
314
+ // 2. Sync URL -> State (Initial Load)
315
+ const { stepId } = useParams();
316
+
317
+ return <WizardProvider config={config} initialStepId={stepId}>...</WizardProvider>;
318
+ ```
319
+
320
+ ### 3. Granular Persistence
321
+
322
+ By default, the wizard uses `MemoryAdapter` (RAM only). You can enable `LocalStorage` globally, but override it or its **behavior** (mode) for individual steps.
323
+
324
+ ```tsx
325
+ const config: IWizardConfig = {
326
+ // Global: Persist everything to LocalStorage on every step change
327
+ persistence: {
328
+ adapter: new LocalStorageAdapter('wizard_'),
329
+ mode: 'onStepChange'
330
+ },
331
+ steps: [
332
+ {
333
+ id: 'public',
334
+ label: 'Public Info',
335
+ // Inherits global settings
336
+ },
337
+ {
338
+ id: 'realtime',
339
+ label: 'Active Draft',
340
+ // Override Mode: Save to local storage on every keystroke
341
+ persistenceMode: 'onChange'
342
+ },
343
+ {
344
+ id: 'sensitive',
345
+ label: 'Credit Card',
346
+ // Override Adapter: Store strictly in memory (cleared on refresh)
347
+ persistenceAdapter: new MemoryAdapter()
348
+ }
349
+ ]
350
+ };
351
+ ```
352
+
353
+ ## API Reference
354
+
355
+ ### `IWizardConfig<T>`
356
+
357
+ - `steps`: Array of step configurations.
358
+ - `persistence`: Configuration for state persistence.
359
+ - `autoValidate`: (obj) Global validation setting.
360
+
361
+ ### `useWizard<T>()`
362
+
363
+ - `activeSteps`: Steps that match conditions.
364
+ - `currentStep`: The currently active step object.
365
+ - `wizardData`: The global state object (subscribe cautiously!).
366
+ - `setData(path, value, options?)`: Update state. Options: `{ debounceValidation: number }`.
367
+ - `getData(path, defaultValue?)`: Retrieve nested data.
368
+ - `handleStepChange(key, value)`: simple helper to update top-level state.
369
+ - `goToNextStep()`: Validates and moves next.
370
+ - `goToStep(id)`: Jumps to specific step.
371
+ - `allErrors`: Map of validation errors.
372
+
373
+ ### New Performance Hooks
374
+
375
+ #### `useWizardValue<T>(path: string)`
376
+
377
+ Subscribes to a specific data path. Re-renders **only** when that value changes.
378
+
379
+ #### `useWizardError(path: string)`
380
+
381
+ Subscribes to validation errors for a specific path. Highly recommended for individual inputs.
382
+
383
+ #### `useWizardSelector<T>(selector: (state: T) => any)`
384
+
385
+ Create a custom subscription to the wizard state. Ideal for derived state or lists.
386
+
387
+ #### `useWizardActions()`
388
+
389
+ Returns object with actions (`setData`, `goToNextStep`, etc.) **without** subscribing to state changes. Use this in components that trigger updates but don't need to render data.
390
+
391
+ ## Demos
392
+
393
+ Check out the [Live Demo](https://ZizzX.github.io/wizzard-stepper-react/), [NPM](https://www.npmjs.com/package/wizzard-stepper-react) or the [source code](https://github.com/ZizzX/wizzard-stepper-react-demo) for a complete implementation featuring:
394
+
395
+ - **Premium Documentation**: [Interactive Guides](https://ZizzX.github.io/wizzard-stepper-react/docs/introduction) and a dedicated [Type Reference](https://ZizzX.github.io/wizzard-stepper-react/docs/types).
396
+ - **Tailwind CSS v4** UI overhaul.
397
+ - **React Hook Form + Zod** integration.
398
+ - **Formik + Yup** integration.
399
+ - **Conditional Routing** logic.
400
+ - **Advanced Features Demo**: (`/advanced`) showcasing:
401
+ - **Autofill**: `updateData` global merge.
402
+ - **Declarative Rendering**: `<WizardStepRenderer />`.
403
+ - **Granular Persistence**: Hybrid Memory/LocalStorage.
404
+
405
+ ## Advanced Demo Guide ๐Ÿงช
406
+
407
+ Visit `/advanced` in the demo to try:
408
+
409
+ 1. **Autofill**: Click "Magic Autofill" to test `updateData()`. It instantly populates the form (merged with existing data).
410
+ 2. **Hybrid Persistence**:
411
+ * **Step 1 (Identity)**: Refreshes persist (LocalStorage).
412
+ * **Step 2 (Security)**: Refreshes CLEAR data (MemoryAdapter).
413
+ 3. **Declarative UI**: The steps are rendered using `<WizardStepRenderer />` with Framer Motion animations, defined in the config!
414
+
415
+ ### โšก Performance & Scalability
416
+
417
+ `wizzard-stepper-react` is architected for extreme scale.
418
+
419
+ - **Stateless Provider**: The main provider doesn't re-render when field values change. Only the specific components subscribed to those fields (via `useWizardValue`) re-render.
420
+ - **O(1) Engine**: Internal lookups for step configuration, active steps, and indices use Hash Maps to ensure instant response times even with hundreds of steps.
421
+ - **Deeply Nested Optimization**: Data access uses a memoized iterative path traversal, avoiding recursive overhead in deeply nested objects.
422
+ - **Loading State**: Built-in `isLoading` state to handle high-latency persistence restoration gracefully.
423
+
424
+ ```tsx
425
+ const { isLoading } = useWizardState();
426
+ if (isLoading) return <SkeletonLoader />;
427
+ ```
428
+
429
+ ## License
430
+
431
+ MIT