react-form-steps 1.0.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Coderkube Technologies
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,542 @@
1
+ # ๐Ÿš€ react-form-steps
2
+
3
+ A premium, production-ready multi-step form manager for **React** & **React Native**. Built on top of `react-hook-form` and `zod`, it handles complex data flows, file persistence, draft recovery, and smooth animations โ€” all with zero configuration.
4
+
5
+ [![npm version](https://img.shields.io/npm/v/react-form-steps.svg)](https://www.npmjs.com/package/react-form-steps)
6
+ [![license](https://img.shields.io/npm/l/react-form-steps.svg)](https://github.com/yourusername/react-form-steps/blob/main/LICENSE)
7
+
8
+ ---
9
+
10
+ ## ๐Ÿ’ป Interactive CLI Scaffolding
11
+
12
+ `react-form-steps` ships with a powerful interactive CLI that generates complete multi-step wizard forms in seconds. No boilerplate โ€” just answer a few questions and start building.
13
+
14
+ <p align="center">
15
+ <a href="https://github.com/user-attachments/assets/c555b874-a716-47b7-9474-c24daa24f09f">
16
+ <img src="assets/cli_screenshot.png" alt="Watch react-form-steps CLI Demo Video" width="800" style="border-radius: 8px; box-shadow: 0 4px 20px rgba(0,0,0,0.15);" />
17
+ </a>
18
+ <br />
19
+ <sub>๐ŸŽฌ <b>Click the terminal preview above to watch the interactive CLI setup demo!</b></sub>
20
+ </p>
21
+
22
+ ```bash
23
+ npx react-form-steps
24
+ ```
25
+
26
+ The CLI walks you through:
27
+
28
+ | Prompt | Options |
29
+ | :--- | :--- |
30
+ | **Platform** | Web (React) ยท React Native (Mobile) |
31
+ | **Language** | JavaScript ยท TypeScript |
32
+ | **Rendering Style** | Normal Inline Page ยท Popup / Modal Overlay |
33
+ | **State Strategy** | Normal (React Hook Form) ยท Redux (React Hook Form + Redux Toolkit) |
34
+ | **Steps** | 1โ€“10 steps |
35
+ | **Fields per Step** | 1โ€“10 fields (uniform across all steps) |
36
+
37
+ ### Generated File Structure
38
+
39
+ ```
40
+ ./form-steps/
41
+ โ”œโ”€โ”€ FormStepsWizard.tsx # Main wizard wrapper
42
+ โ”œโ”€โ”€ Step1.tsx # Step 1 component
43
+ โ”œโ”€โ”€ Step2.tsx # Step 2 component
44
+ โ”œโ”€โ”€ Step3.tsx # Step 3 component
45
+ โ”œโ”€โ”€ form-steps.css # (Web only) Prebuilt styles
46
+ โ”œโ”€โ”€ formSlice.ts # (Redux only) Redux Toolkit slice
47
+ โ””โ”€โ”€ index.ts # Barrel export
48
+ ```
49
+
50
+ ---
51
+
52
+ ## ๐ŸŒŸ Key Features
53
+
54
+ - ๐Ÿ—๏ธ **Simple Architecture** โ€” Use `<FormSteps>` and `<Step>` components to build forms in minutes.
55
+ - โœ… **First-Class Validation** โ€” Optional Zod integration for per-step or global validation.
56
+ - ๐Ÿ’พ **Smart Persistence** โ€” Automatically saves drafts to `localStorage`, `sessionStorage`, `AsyncStorage`, or your custom database.
57
+ - ๐Ÿ“‚ **Base64 File Helper** โ€” Serializes `File` objects (images/PDFs) into drafts and restores them as real Files on resume.
58
+ - โœจ **Native Animations** โ€” Beautiful built-in `slide` and `fade` transitions.
59
+ - ๐Ÿ“Š **Analytics Built-in** โ€” Track user drop-off with `onStepEnter` and `onStepComplete` callbacks.
60
+ - ๐Ÿ”„ **Edit Mode** โ€” Switch between "Create" and "Edit" modes with automatic field diffing.
61
+ - ๐ŸŽจ **Fully Customizable** โ€” Render props for banners, sidebars, and complete UI control.
62
+ - ๐Ÿ“ฑ **Cross-Platform** โ€” First-class React Native support with platform-specific components.
63
+
64
+ ---
65
+
66
+ ## ๐Ÿ“ฆ Installation
67
+
68
+ ```bash
69
+ # Core dependencies
70
+ npm install react-form-steps react-hook-form @hookform/resolvers
71
+
72
+ # Optional: Add Zod for schema validation
73
+ npm install zod
74
+ ```
75
+
76
+ For **React Native**, also install:
77
+ ```bash
78
+ npm install @react-native-async-storage/async-storage
79
+ ```
80
+
81
+ ---
82
+
83
+ ## ๐Ÿš€ Quick Start (Web)
84
+
85
+ ### 1. Define Your Step Components
86
+
87
+ ```tsx
88
+ // steps/PersonalInfo.tsx
89
+ import { useFormSteps } from 'react-form-steps';
90
+
91
+ export function PersonalInfo() {
92
+ const { register, formState: { errors }, goBack, goNext } = useFormSteps();
93
+
94
+ return (
95
+ <div>
96
+ <h3>Personal Information</h3>
97
+
98
+ <div>
99
+ <label>Full Name</label>
100
+ <input
101
+ {...register('fullName', { required: 'Name is required' })}
102
+ placeholder="John Doe"
103
+ />
104
+ {errors.fullName && <span>{errors.fullName.message}</span>}
105
+ </div>
106
+
107
+ <div>
108
+ <label>Email Address</label>
109
+ <input
110
+ type="email"
111
+ {...register('email', { required: 'Email is required' })}
112
+ placeholder="john@example.com"
113
+ />
114
+ {errors.email && <span>{errors.email.message}</span>}
115
+ </div>
116
+
117
+ <div>
118
+ <button type="submit">Next Step</button>
119
+ </div>
120
+ </div>
121
+ );
122
+ }
123
+ ```
124
+
125
+ ```tsx
126
+ // steps/AddressInfo.tsx
127
+ import { useFormSteps } from 'react-form-steps';
128
+
129
+ export function AddressInfo() {
130
+ const { register, formState: { errors }, goBack } = useFormSteps();
131
+
132
+ return (
133
+ <div>
134
+ <h3>Address Details</h3>
135
+
136
+ <div>
137
+ <label>Street Address</label>
138
+ <input
139
+ {...register('street', { required: 'Street is required' })}
140
+ placeholder="123 Main St"
141
+ />
142
+ {errors.street && <span>{errors.street.message}</span>}
143
+ </div>
144
+
145
+ <div>
146
+ <label>City</label>
147
+ <input
148
+ {...register('city', { required: 'City is required' })}
149
+ placeholder="New York"
150
+ />
151
+ {errors.city && <span>{errors.city.message}</span>}
152
+ </div>
153
+
154
+ <div>
155
+ <button type="button" onClick={goBack}>Back</button>
156
+ <button type="submit">Submit</button>
157
+ </div>
158
+ </div>
159
+ );
160
+ }
161
+ ```
162
+
163
+ ### 2. Compose the Wizard
164
+
165
+ ```tsx
166
+ // App.tsx
167
+ import { FormSteps, Step } from 'react-form-steps';
168
+ import { PersonalInfo } from './steps/PersonalInfo';
169
+ import { AddressInfo } from './steps/AddressInfo';
170
+
171
+ function App() {
172
+ const handleSubmit = (payload: any, diff: any) => {
173
+ console.log('โœ… Form submitted:', payload);
174
+ // Send to your API
175
+ };
176
+
177
+ return (
178
+ <FormSteps
179
+ formKey="user-registration"
180
+ storageStrategy="localStorage"
181
+ transition="slide"
182
+ allowJump={true}
183
+ onSubmit={handleSubmit}
184
+ onStepEnter={(idx) => console.log('Entered step', idx)}
185
+ onStepComplete={(idx) => console.log('Completed step', idx)}
186
+ >
187
+ <Step label="Personal Info">
188
+ <PersonalInfo />
189
+ </Step>
190
+
191
+ <Step label="Address">
192
+ <AddressInfo />
193
+ </Step>
194
+ </FormSteps>
195
+ );
196
+ }
197
+
198
+ export default App;
199
+ ```
200
+
201
+ ---
202
+
203
+ ## ๐Ÿ“ฑ Quick Start (React Native)
204
+
205
+ ### 1. Define Your Step Components
206
+
207
+ ```tsx
208
+ // steps/PersonalInfo.tsx
209
+ import React from 'react';
210
+ import { View, Text, TextInput, TouchableOpacity, StyleSheet } from 'react-native';
211
+ import { useFormSteps } from 'react-form-steps';
212
+
213
+ export function PersonalInfo() {
214
+ const { setValue, watch, formState: { errors }, goNext } = useFormSteps();
215
+
216
+ return (
217
+ <View style={styles.container}>
218
+ <Text style={styles.title}>Personal Information</Text>
219
+
220
+ <View style={styles.field}>
221
+ <Text style={styles.label}>Full Name</Text>
222
+ <TextInput
223
+ style={styles.input}
224
+ placeholder="John Doe"
225
+ value={watch('fullName') || ''}
226
+ onChangeText={(val) => setValue('fullName', val, { shouldValidate: true })}
227
+ />
228
+ {errors.fullName && (
229
+ <Text style={styles.error}>{errors.fullName.message}</Text>
230
+ )}
231
+ </View>
232
+
233
+ <View style={styles.field}>
234
+ <Text style={styles.label}>Email</Text>
235
+ <TextInput
236
+ style={styles.input}
237
+ placeholder="john@example.com"
238
+ keyboardType="email-address"
239
+ value={watch('email') || ''}
240
+ onChangeText={(val) => setValue('email', val, { shouldValidate: true })}
241
+ />
242
+ {errors.email && (
243
+ <Text style={styles.error}>{errors.email.message}</Text>
244
+ )}
245
+ </View>
246
+
247
+ <TouchableOpacity style={styles.button} onPress={goNext}>
248
+ <Text style={styles.buttonText}>Next Step</Text>
249
+ </TouchableOpacity>
250
+ </View>
251
+ );
252
+ }
253
+
254
+ const styles = StyleSheet.create({
255
+ container: { padding: 16 },
256
+ title: { fontSize: 20, fontWeight: '700', marginBottom: 16, color: '#1e293b' },
257
+ field: { marginBottom: 16 },
258
+ label: { fontSize: 13, fontWeight: '600', color: '#64748b', marginBottom: 6 },
259
+ input: { borderWidth: 1, borderColor: '#cbd5e1', borderRadius: 8, padding: 12, fontSize: 14 },
260
+ error: { color: '#ef4444', fontSize: 12, marginTop: 4 },
261
+ button: { backgroundColor: '#3b82f6', padding: 14, borderRadius: 8, alignItems: 'center', marginTop: 12 },
262
+ buttonText: { color: '#fff', fontSize: 14, fontWeight: '600' },
263
+ });
264
+ ```
265
+
266
+ ### 2. Compose the Wizard
267
+
268
+ ```tsx
269
+ // App.tsx
270
+ import React from 'react';
271
+ import { SafeAreaView, ScrollView } from 'react-native';
272
+ import AsyncStorage from '@react-native-async-storage/async-storage';
273
+ import { FormSteps, Step } from 'react-form-steps';
274
+ import { PersonalInfo } from './steps/PersonalInfo';
275
+ import { AddressInfo } from './steps/AddressInfo';
276
+
277
+ export default function App() {
278
+ const handleSubmit = (payload: any) => {
279
+ console.log('โœ… Form submitted:', payload);
280
+ };
281
+
282
+ return (
283
+ <SafeAreaView style={{ flex: 1 }}>
284
+ <ScrollView>
285
+ <FormSteps
286
+ formKey="native-registration"
287
+ asyncStorage={AsyncStorage}
288
+ transition="slide"
289
+ allowJump={true}
290
+ onSubmit={handleSubmit}
291
+ >
292
+ <Step label="Personal Info">
293
+ <PersonalInfo />
294
+ </Step>
295
+
296
+ <Step label="Address">
297
+ <AddressInfo />
298
+ </Step>
299
+ </FormSteps>
300
+ </ScrollView>
301
+ </SafeAreaView>
302
+ );
303
+ }
304
+ ```
305
+
306
+ ---
307
+
308
+ ## ๐Ÿ› ๏ธ Advanced Features
309
+
310
+ ### ๐Ÿ’พ Persistence & Draft Recovery
311
+
312
+ The library auto-saves user progress as they navigate between steps. If a user leaves and returns later, a customizable banner asks them to resume or start fresh.
313
+
314
+ ```tsx
315
+ <FormSteps
316
+ formKey="checkout-form"
317
+ storageStrategy="localStorage" // Web: localStorage or sessionStorage
318
+ asyncStorage={AsyncStorage} // React Native: AsyncStorage
319
+ draftTTL={86400} // Draft expires after 24 hours (in seconds)
320
+ Autofilldata={true} // Skip the banner, auto-resume silently
321
+ onDraftFound={(draft) => console.log('Draft found!', draft)}
322
+ onSubmit={handleSubmit}
323
+ >
324
+ {/* steps */}
325
+ </FormSteps>
326
+ ```
327
+
328
+ ### โœ… Zod Schema Validation
329
+
330
+ Pass a Zod schema to any `<Step>` for per-step validation. The form will not advance until the schema passes:
331
+
332
+ ```tsx
333
+ import { z } from 'zod';
334
+
335
+ const contactSchema = z.object({
336
+ phone: z.string().min(10, 'Enter a valid phone number'),
337
+ address: z.string().min(5, 'Address is too short'),
338
+ });
339
+
340
+ <Step label="Contact Details" schema={contactSchema}>
341
+ <ContactForm />
342
+ </Step>
343
+ ```
344
+
345
+ ### ๐Ÿ“‚ Automatic File Persistence
346
+
347
+ Most libraries lose file uploads if the page refreshes. `react-form-steps` automatically converts `File` and `FileList` objects into Base64 strings for storage, and restores them as real `File` objects when the user resumes.
348
+
349
+ ```tsx
350
+ // In your step component โ€” just use a file input normally:
351
+ <input type="file" {...register('avatar')} />
352
+
353
+ // The library automatically serializes & restores files from drafts!
354
+ ```
355
+
356
+ ### ๐Ÿ”„ Edit Mode
357
+
358
+ Pass `defaultValues` to switch to edit mode. The library automatically tracks which fields changed:
359
+
360
+ ```tsx
361
+ <FormSteps
362
+ formKey="edit-profile"
363
+ defaultValues={existingUserData} // Activates edit mode
364
+ onSubmit={(payload, changedFields) => {
365
+ console.log('Full payload:', payload);
366
+ console.log('Only changed fields:', changedFields);
367
+ // Send a PATCH request with only the changed fields
368
+ }}
369
+ >
370
+ {/* steps */}
371
+ </FormSteps>
372
+ ```
373
+
374
+ ### ๐Ÿ—„๏ธ Redux Integration
375
+
376
+ Sync form state with your Redux store in real-time:
377
+
378
+ ```tsx
379
+ import { useDispatch } from 'react-redux';
380
+ import { updateFormData } from './formSlice';
381
+
382
+ function App() {
383
+ const dispatch = useDispatch();
384
+
385
+ return (
386
+ <FormSteps
387
+ formKey="redux-form"
388
+ onDataChange={(data) => dispatch(updateFormData(data))}
389
+ onSubmit={handleSubmit}
390
+ >
391
+ {/* steps */}
392
+ </FormSteps>
393
+ );
394
+ }
395
+ ```
396
+
397
+ ### ๐Ÿ“Š Analytics & Tracking
398
+
399
+ Monitor conversion rates and user drop-off:
400
+
401
+ ```tsx
402
+ <FormSteps
403
+ onStepEnter={(idx) => analytics.track('Step Viewed', { step: idx })}
404
+ onStepComplete={(idx) => analytics.track('Step Completed', { step: idx })}
405
+ onSubmit={handleSubmit}
406
+ >
407
+ {/* steps */}
408
+ </FormSteps>
409
+ ```
410
+
411
+ ### โœจ Transitions
412
+
413
+ Add smooth animations between steps:
414
+
415
+ ```tsx
416
+ <FormSteps transition="slide"> {/* Slide from right */}
417
+ <FormSteps transition="fade"> {/* Fade in/out */}
418
+ <FormSteps transition="none"> {/* Instant (default) */}
419
+ ```
420
+
421
+ ### ๐Ÿ”€ Step Navigation
422
+
423
+ Control how users can navigate between steps:
424
+
425
+ ```tsx
426
+ <FormSteps
427
+ allowJump={true} // Allow clicking step indicators to jump
428
+ unrestrictedNav={true} // Allow jumping even to incomplete steps
429
+ onSubmit={handleSubmit}
430
+ >
431
+ {/* steps */}
432
+ </FormSteps>
433
+ ```
434
+
435
+ ---
436
+
437
+ ## ๐Ÿ“– API Reference
438
+
439
+ ### `<FormSteps />` Props
440
+
441
+ | Prop | Type | Default | Description |
442
+ | :--- | :--- | :--- | :--- |
443
+ | `formKey` | `string` | โ€” | Unique key for draft storage. |
444
+ | `storageStrategy` | `'localStorage' \| 'sessionStorage' \| 'database' \| 'none'` | `'none'` | Where to persist drafts (Web). |
445
+ | `asyncStorage` | `{ getItem, setItem, removeItem }` | โ€” | Custom async storage adapter (React Native). |
446
+ | `Autofilldata` | `boolean` | `false` | Auto-resume drafts without prompting the user. |
447
+ | `draftTTL` | `number` | โ€” | Draft time-to-live in seconds. |
448
+ | `transition` | `'slide' \| 'fade' \| 'none'` | `'none'` | Animation between steps. |
449
+ | `allowJump` | `boolean` | `false` | Allow non-linear step navigation. |
450
+ | `unrestrictedNav` | `boolean` | `false` | Allow jumping to incomplete steps. |
451
+ | `defaultValues` | `any` | โ€” | Initial data (activates Edit Mode). |
452
+ | `onSubmit` | `(payload, diff) => void` | **Required** | Called on final step submission. |
453
+ | `onAutoSave` | `(step, data, merged) => Promise<void>` | โ€” | Custom auto-save callback (database strategy). |
454
+ | `onClearDraft` | `() => void` | โ€” | Called when draft is cleared (clean up DB). |
455
+ | `onDataChange` | `(data) => void` | โ€” | Sync state with Redux or external stores. |
456
+ | `onDraftFound` | `(draft) => void` | โ€” | Notified when a saved draft is detected. |
457
+ | `onStepEnter` | `(index) => void` | โ€” | Called when a step becomes active. |
458
+ | `onStepComplete` | `(index) => void` | โ€” | Called when a step is completed. |
459
+ | `bannerConfig` | `object` | โ€” | Customize default banner text and styles. |
460
+ | `renderDraftBanner` | `(props) => ReactNode` | โ€” | Render prop for fully custom draft banners. |
461
+
462
+ ### `<Step />` Props
463
+
464
+ | Prop | Type | Default | Description |
465
+ | :--- | :--- | :--- | :--- |
466
+ | `label` | `string` | **Required** | Display name for the step. |
467
+ | `schema` | `ZodSchema` | โ€” | Zod schema for per-step validation. |
468
+
469
+ ### `useFormSteps()` Hook
470
+
471
+ Returns the full `FormStepsContext` merged with `react-hook-form`'s `useFormContext()`:
472
+
473
+ | Value | Type | Description |
474
+ | :--- | :--- | :--- |
475
+ | `values` / `mergedData` | `any` | Current merged values of all steps. |
476
+ | `currentStep` | `number` | Index of the active step. |
477
+ | `steps` | `StepInfo[]` | Array of `{ index, label, status }`. |
478
+ | `isEditMode` | `boolean` | `true` when `defaultValues` is provided. |
479
+ | `isSubmitting` | `boolean` | `true` during `onSubmit` execution. |
480
+ | `changedFields` | `Record<string, boolean>` | Map of changed fields (edit mode only). |
481
+ | `goNext()` | `() => Promise<void>` | Validate current step and move forward. |
482
+ | `goBack()` | `() => void` | Move to previous step. |
483
+ | `goToStep(idx)` | `(index: number) => void` | Jump to a specific step. |
484
+ | `getAllErrors()` | `() => Record<number, any>` | Get validation errors across all steps. |
485
+ | `resumeDraft(draft)` | `(draft) => void` | Programmatically resume a draft. |
486
+ | `clearDraft()` | `() => void` | Clear saved draft and reset form. |
487
+ | `register` | `function` | Register input fields (from React Hook Form). |
488
+ | `watch` | `function` | Watch specific fields (from React Hook Form). |
489
+ | `setValue` | `function` | Set field values (from React Hook Form). |
490
+ | `handleSubmit` | `function` | Form submit handler (from React Hook Form). |
491
+ | `formState` | `object` | Full form state including `errors`, `isDirty`, etc. |
492
+
493
+ ---
494
+
495
+ ## ๐ŸŽจ Customizing the Draft Banner
496
+
497
+ Don't like the default banner? Replace it entirely with your own UI:
498
+
499
+ ```tsx
500
+ <FormSteps
501
+ formKey="my-form"
502
+ storageStrategy="localStorage"
503
+ renderDraftBanner={({ draft, resume, startFresh, dismiss }) => (
504
+ <div className="my-custom-banner">
505
+ <p>๐Ÿ“ We found your previous progress!</p>
506
+ <p>Last saved: {new Date(draft.savedAt).toLocaleString()}</p>
507
+ <button onClick={resume}>Continue Where I Left Off</button>
508
+ <button onClick={startFresh}>Start Over</button>
509
+ <button onClick={dismiss}>Dismiss</button>
510
+ </div>
511
+ )}
512
+ onSubmit={handleSubmit}
513
+ >
514
+ {/* steps */}
515
+ </FormSteps>
516
+ ```
517
+
518
+ Or customize just the text using `bannerConfig`:
519
+
520
+ ```tsx
521
+ <FormSteps
522
+ bannerConfig={{
523
+ title: 'Welcome Back!',
524
+ description: 'You have unsaved progress from your last visit.',
525
+ resumeLabel: 'Continue',
526
+ freshLabel: 'Start Over',
527
+ }}
528
+ onSubmit={handleSubmit}
529
+ >
530
+ {/* steps */}
531
+ </FormSteps>
532
+ ```
533
+
534
+ ---
535
+
536
+ ## ๐Ÿค Contributing
537
+
538
+ Contributions are welcome! Please open an issue or submit a pull request.
539
+
540
+ ## ๐Ÿ“„ License
541
+
542
+ MIT ยฉ [CoderKube Technologies](https://coderkube.com)