Multi Step Form
boldkit/multi-step-formFreeComponent
Dependency-free multi-step form state layer with per-step validation, designed to compose with Stepper
Live preview
live · rendered by the registry
Rendered by boldkit on their own page — this is the component running, not a screenshot of it.Install it
npx shadcn@latest add https://boldkit.dev/r/multi-step-form.jsonSource
1/* eslint-disable react-refresh/only-export-components */2import * as React from 'react'34/**5 * Multi-step form: a dependency-free form-state layer designed to sit on top of6 * the <Stepper /> UI. It owns values, per-field errors, touched state, and the7 * active step, and gates forward navigation on the current step's validator.8 * Stepper renders the indicator; this drives its `activeStep`.9 */1011export type StepValidator<V> = (values: V) => Record<string, string> | null1213export interface MultiStepFormStep<V> {14 id: string15 validate?: StepValidator<V>16}1718interface MultiStepFormContextValue<V> {19 values: V20 setValue: <K extends keyof V>(name: K, value: V[K]) => void21 errors: Record<string, string>22 touched: Record<string, boolean>23 activeStep: number24 next: () => void25 back: () => void26 goTo: (step: number) => void27 canGoNext: boolean28 isStepValid: boolean29 isFirstStep: boolean30 isLastStep: boolean31 submit: () => void32}3334// One context object reused for every value type; consumers read it through the35// typed useMultiStepForm<V>() hook below.36const MultiStepFormContext = React.createContext<MultiStepFormContextValue<unknown> | null>(null)3738export interface MultiStepFormProps<V> {39 steps: MultiStepFormStep<V>[]40 initialValues: V41 onSubmit: (values: V) => void42 children: React.ReactNode43}4445export function MultiStepForm<V>({46 steps,47 initialValues,48 onSubmit,49 children,50}: MultiStepFormProps<V>) {51 const [values, setValues] = React.useState<V>(initialValues)52 const [errors, setErrors] = React.useState<Record<string, string>>({})53 const [touched, setTouched] = React.useState<Record<string, boolean>>({})54 const [activeStep, setActiveStep] = React.useState(0)5556 const totalSteps = steps.length57 const isFirstStep = activeStep === 058 const isLastStep = activeStep === totalSteps - 15960 const setValue = React.useCallback(<K extends keyof V>(name: K, value: V[K]) => {61 setValues((prev) => ({ ...prev, [name]: value }))62 setTouched((prev) => ({ ...prev, [name as string]: true }))63 setErrors((prev) => {64 if (!(name as string in prev)) return prev65 const next = { ...prev }66 delete next[name as string]67 return next68 })69 }, [])7071 // Validate the current step against the latest values. Returns true when the72 // step has no validator or the validator returns null; otherwise publishes73 // the errors and returns false.74 const validateStep = React.useCallback(75 (step: number): boolean => {76// … truncated
What it pulls in
Other registry items
Files it writes
More from boldkit
All 94 items| Component | Registry | Kind | Access | Installs | Command |
|---|---|---|---|---|---|
| Accordionaccordion | boldkit | Components | Free | 2 deps | |
| Alertalert | boldkit | Components | Free | 2 deps | |
| Alert Dialogalert-dialog | boldkit | Components | Free | 1 dep | |
| ASCII Shapesascii-shapes | boldkit | Components | Free | no deps | |
| Aspect Ratioaspect-ratio | boldkit | Components | Free | 1 dep | |
| Avataravatar | boldkit | Components | Free | 1 dep | |
| Badgebadge | boldkit | Components | Free | 1 dep | |
| Breadcrumbbreadcrumb | boldkit | Components | Free | 2 deps |
