@mshafiqyajid/react-stepper
v0.3.1
Published
Multi-step wizard / stepper hook and styled component for React. Linear or non-linear, validation gates, progress indicator. Zero dependencies, fully typed.
Maintainers
Readme
@mshafiqyajid/react-stepper
Multi-step wizard / stepper for React. Headless useStepper hook + styled component. Linear or non-linear progression, validation gates, async-aware, horizontal or vertical layout. Zero dependencies, fully typed.
Install
npm install @mshafiqyajid/react-stepperQuick start (styled)
import { StepperStyled } from "@mshafiqyajid/react-stepper/styled";
import "@mshafiqyajid/react-stepper/styles.css";
<StepperStyled
steps={[
{ id: "account", label: "Account", description: "Email + password" },
{
id: "billing",
label: "Billing",
description: "Card details",
validate: () => formIsValid || "Add a payment method",
},
{ id: "review", label: "Review" },
]}
content={{
account: <AccountForm />,
billing: <BillingForm />,
review: <ReviewSummary />,
}}
onFinish={() => submit()}
/>Headless
import { useStepper } from "@mshafiqyajid/react-stepper";
const stepper = useStepper({ steps, mode: "linear" });
<>
{steps.map((s, i) => (
<button
key={s.id}
onClick={() => stepper.goTo(i)}
aria-current={stepper.activeStep === i ? "step" : undefined}
disabled={s.disabled}
>
{s.label}
</button>
))}
<div>{content[stepper.activeStepId]}</div>
<button onClick={() => void stepper.goNext()}>Next</button>
</>Features
- Linear / non-linear —
mode: "linear"(default) requires sequential progression;"non-linear"allows arbitrary jumps. - Validation gates — each step accepts a
validate: () => boolean | string | Promise<...>. Returntrueto allowgoNext; return a string to seterror. - Async-aware —
validatemay return aPromise. The hook tracksisPendingso the UI can disable controls while validating. - Horizontal or vertical layout via
orientation. - Disabled steps — set
disabled: trueon a step to skip it during prev/next navigation. - Reset —
stepper.reset()returns to the first step + clears completed/visited state.
Props (styled)
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| steps | StepperStep[] | — | Required |
| content | Record<id, ReactNode> | — | Step content keyed by step id |
| renderContent | (ctx) => ReactNode | — | Alternative to content |
| renderStep | (ctx) => ReactNode | — | Custom step indicator |
| orientation | "horizontal" \| "vertical" | "horizontal" | Layout direction |
| size | "sm" \| "md" \| "lg" | "md" | Indicator size |
| tone | "neutral" \| "primary" | "primary" | Active accent |
| mode | "linear" \| "non-linear" | "linear" | Navigation mode |
| defaultStep / step / onStepChange | controlled state | — | Active step |
| defaultCompleted / completed / onCompletedChange | controlled completed ids | — | — |
| onFinish | () => void | — | Fires on Finish (after final-step validate) |
| showFooter | boolean | true | Built-in Back / Next / Finish buttons |
| clickableSteps | boolean | true | Allow clicking visited / earlier steps |
| progressBar | boolean | false | Render a thin progress bar (completed / total) above the steps |
| labels | { back?, next?, finish?, optional? } | — | Customise footer button + "(optional)" labels |
StepperStep
| Field | Type | Description |
|-------|------|-------------|
| id | string | Required, unique |
| label | ReactNode | Required |
| description | ReactNode? | Sub-text below label |
| icon | ReactNode? | Custom indicator icon |
| disabled | boolean? | Skipped by prev/next |
| optional | boolean? | Renders "(optional)" next to the label |
| error | boolean? | Mark indicator as failed (shake animation, danger ring) |
| validate | () => boolean \| string \| Promise<...> | Run before leaving this step |
License
MIT
