Project: Multi-Step Wizard Form
In this project, we will construct a multi-step user registration form. The state, current progress step, and input fields values are stored inside a central Zustand store, allowing users to navigate back and forth without losing inputted details.
1. Wizard Specifications
- Step 1: Account Information: Username and password inputs.
- Step 2: Profile Details: First name, last name, and contact email.
- Step 3: Summary and Submission: Review all fields and execute submission action.
2. Implementing the Wizard Store
Create a store file named useFormStore.ts inside src/stores/:
// src/stores/useFormStore.ts
import { create } from "zustand";
interface FormData {
username: string;
email: string;
fullName: string;
}
interface FormState {
currentStep: number;
data: FormData;
nextStep: () => void;
prevStep: () => void;
updateFields: (fields: Partial<FormData>) => void;
resetForm: () => void;
}
export const useFormStore = create<FormState>((set) => ({
currentStep: 1,
data: {
username: "",
email: "",
fullName: "",
},
nextStep: () => set((state) => ({ currentStep: state.currentStep + 1 })),
prevStep: () => set((state) => ({ currentStep: Math.max(1, state.currentStep - 1) })),
updateFields: (fields) => set((state) => ({
data: { ...state.data, ...fields }
})),
resetForm: () => set({
currentStep: 1,
data: { username: "", email: "", fullName: "" }
}),
}));3. Implementing the Form Component
Here is the React wizard form markup layout:
// src/components/RegistrationWizard.tsx
import { useFormStore } from "../stores/useFormStore";
export default function RegistrationWizard() {
const { currentStep, data, nextStep, prevStep, updateFields, resetForm } = useFormStore();
const handleFormSubmit = (e: React.FormEvent) => {
e.preventDefault();
console.log("Submitting registration profile data:", data);
alert("Registration successfully completed!");
resetForm();
};
return (
<div className="max-w-md mx-auto p-8 border rounded-xl bg-white shadow-sm mt-12">
<div className="flex justify-between text-sm text-gray-500 mb-6">
<span>Step {currentStep} of 3</span>
<span className="font-semibold">Progress: {Math.round((currentStep / 3) * 100)}%</span>
</div>
<form onSubmit={handleFormSubmit}>
{/* Step 1: Account Setup */}
{currentStep === 1 && (
<div className="space-y-4">
<h2 className="text-xl font-bold">Account Setup</h2>
<input
type="text"
value={data.username}
onChange={(e) => updateFields({ username: e.target.value })}
placeholder="Username"
className="w-full border p-3 rounded-lg"
required
/>
</div>
)}
{/* Step 2: Personal Profile Details */}
{currentStep === 2 && (
<div className="space-y-4">
<h2 className="text-xl font-bold">Personal Profile</h2>
<input
type="text"
value={data.fullName}
onChange={(e) => updateFields({ fullName: e.target.value })}
placeholder="Full Name"
className="w-full border p-3 rounded-lg"
required
/>
<input
type="email"
value={data.email}
onChange={(e) => updateFields({ email: e.target.value })}
placeholder="Email Address"
className="w-full border p-3 rounded-lg"
required
/>
</div>
)}
{/* Step 3: Review Confirmation */}
{currentStep === 3 && (
<div className="space-y-4">
<h2 className="text-xl font-bold">Review Details</h2>
<div className="p-4 bg-gray-50 rounded-lg space-y-2 text-sm">
<p><strong>Username:</strong> {data.username}</p>
<p><strong>Full Name:</strong> {data.fullName}</p>
<p><strong>Email:</strong> {data.email}</p>
</div>
</div>
)}
{/* Action controls buttons */}
<div className="flex justify-between mt-8">
{currentStep > 1 && (
<button type="button" onClick={prevStep} className="border px-4 py-2 rounded-lg">
Back
</button>
)}
{currentStep < 3 ? (
<button type="button" onClick={nextStep} className="bg-blue-600 text-white px-6 py-2 rounded-lg ml-auto">
Next Step
</button>
) : (
<button type="submit" className="bg-green-600 text-white px-6 py-2 rounded-lg ml-auto">
Submit registration
</button>
)}
</div>
</form>
</div>
);
}Published on Last updated: