Scoped
Build a stepper with Scoped and useStepper (single or nested multi-scoped) without primitives.
Use Scoped and useStepper() so any descendant can read and control the same stepper—no primitives, just your own UI. You can use a single Scoped (one stepper) or nest two or more Scoped providers (e.g. global wizard + local sub-steps). The tabs below switch between the single-scope and multi-scoped patterns.
Define your steps (and get Scoped / useStepper)
Use defineStepper with one object per step. For a single stepper you destructure Scoped and useStepper. For multi-scoped you create two (or more) steppers; each has its own Scoped and useStepper.
One stepper: wrap your tree with <Scoped> and call useStepper() in children.
import * as React from "react";
import { } from "@stepperize/react";
const { , } = (
{ : "info", : "Info" },
{ : "review", : "Review" },
{ : "done", : "Done" }
);Two steppers: a global (e.g. wizard) and a local one (sub-steps). Each has its own Scoped and useStepper.
import { } from "@stepperize/react";
const = (
{ : "start", : "Start" },
{ : "middle", : "Middle" },
{ : "end", : "End" }
);
const = (
{ : "sub1", : "Sub-step 1" },
{ : "sub2", : "Sub-step 2" }
);Wrap your UI with Scoped
Scoped is the provider: it holds the stepper state. Any component inside it can call the matching useStepper() and get that stepper instance. For multi-scoped, nest the local Scoped inside the global one so inner components can use both.
import * as React from "react";
import { } from "@stepperize/react";
const { , } = (
{ : "info", : "Info" },
{ : "review", : "Review" },
{ : "done", : "Done" }
);
function () {
return (
<>
< />
< />
</>
);
}
function () {
const = ();
return <>{/* content per step */}</>;
}
function () {
const = ();
return <>{/* buttons */}</>;
}import * as React from "react";
import { } from "@stepperize/react";
const = (
{ : "start", : "Start" },
{ : "middle", : "Middle" },
{ : "end", : "End" }
);
const = (
{ : "sub1", : "Sub-step 1" },
{ : "sub2", : "Sub-step 2" }
);
export function () {
return (
<.>
< />
<.>
< />
< />
</.>
< />
</.>
);
}
function () {
const = .();
return <>Global: {....}</>;
}
function () {
const = .();
return (
<>
{!.. && (
< ="button" ={() => ..()}>Back</>
)}
{!.. && (
< ="button" ={() => ..()}>Next</>
)}
</>
);
}
function () {
const = .();
return <>Local: {....}</>;
}
function () {
const = .();
return (
<>
< ="button" ={() => ..()} ={..}>
Prev sub
</>
< ="button" ={() => ..()} ={..}>
Next sub
</>
</>
);
}Render content per step (flow.switch / flow.when)
Inside a child component, call useStepper() (or GlobalStepper.useStepper() / LocalStepper.useStepper() in multi-scoped) and use stepper.flow.switch({ ... }) to render different content per step. You can also use flow.when(id, fn) or flow.is(id).
For single scope the pattern is the same in every child. For multi-scoped, global content uses the global stepper's flow.switch; local content uses the local stepper's flow.switch. Each stepper has its own state.
import * as React from "react";
import { } from "@stepperize/react";
const { , } = (
{ : "info", : "Info" },
{ : "review", : "Review" },
{ : "done", : "Done" }
);
function () {
const = ();
return (
<>
{..({
: () => <>Step: {.}. Enter your info.</>,
: () => <>Review your data.</>,
: () => <>All done!</>,
})}
</>
);
}Global content uses GlobalStepper.useStepper() and its flow.switch. Inside the local Scoped, local content uses LocalStepper.useStepper() and its flow.switch. You can show the local scope only on a given global step (e.g. "middle") by conditionally rendering the local Scoped when globalStepper.flow.is("middle").
Add navigation (next / prev / reset)
In another child component, call the same useStepper() (or the matching one in multi-scoped) and use stepper.state.isFirst, stepper.state.isLast, and stepper.navigation.next(), prev(), reset().
import * as React from "react";
import { } from "@stepperize/react";
const { , } = (
{ : "info", : "Info" },
{ : "review", : "Review" },
{ : "done", : "Done" }
);
function () {
const = ();
return (
<>
{.. ? (
< ="button" ={() => ..()}>
Reset
</>
) : (
<>
{!.. && (
< ="button" ={() => ..()}>
Back
</>
)}
< ="button" ={() => ..()}>
Next
</>
</>
)}
</>
);
}Use GlobalStepper.useStepper() for global nav (Back / Next / Reset) and LocalStepper.useStepper() for local nav (Prev sub / Next sub). Advancing one stepper does not change the other.
Optional: initial step, metadata, or local scope only on one global step
Pass initialStep and/or initialMetadata to Scoped to set the starting step and initial metadata per step.
import * as React from "react";
import { } from "@stepperize/react";
const { , } = (
{ : "info", : "Info" },
{ : "review", : "Review" },
{ : "done", : "Done" }
);
function () {
const = ();
return <>{/* content per step */}</>;
}
function () {
const = ();
return <>{/* buttons */}</>;
}
function () {
return (
<
="review"
={{ : { : "Jane" } }}
>
< />
< />
</>
);
}You can show the local Scoped (and local step UI) only when the global stepper is on a specific step. Use a wrapper that calls GlobalStepper.useStepper() and returns the local scope only when stepper.flow.is("middle").
function GlobalStepGate({ children }: { children: React.ReactNode }) {
const globalStepper = GlobalStepper.useStepper();
if (!globalStepper.flow.is("middle")) return null;
return <>{children}</>;
}
// In your tree:
<GlobalStepper.Scoped>
<GlobalStepContent />
<GlobalStepGate>
<LocalStepper.Scoped>
<LocalStepContent />
<LocalStepNavigation />
</LocalStepper.Scoped>
</GlobalStepGate>
<GlobalStepNavigation />
</GlobalStepper.Scoped>Live preview
Current: Info
Enter your info.
Global step: Start
Start content.
Full example
import * as React from "react";
import { } from "@stepperize/react";
const { , } = (
{ : "info", : "Info" },
{ : "review", : "Review" },
{ : "done", : "Done" }
);
function () {
const = ();
return (
<>
<>Current: {....}</>
{..({
: () => <>Enter your info.</>,
: () => <>Review your data.</>,
: () => <>All done!</>,
})}
</>
);
}
function () {
const = ();
return (
<>
{.. ? (
< ="button" ={() => ..()}>
Reset
</>
) : (
<>
{!.. && (
< ="button" ={() => ..()}>
Back
</>
)}
< ="button" ={() => ..()}>
Next
</>
</>
)}
</>
);
}
export function () {
return (
<>
< />
< />
</>
);
}import * as React from "react";
import { } from "@stepperize/react";
const = (
{ : "start", : "Start" },
{ : "middle", : "Middle" },
{ : "end", : "End" }
);
const = (
{ : "sub1", : "Sub-step 1" },
{ : "sub2", : "Sub-step 2" }
);
function () {
const = .();
return (
<>
<>Global step: {....}</>
{..({
: () => <>Start content.</>,
: () => (
<.>
< />
< />
</.>
),
: () => <>End content.</>,
})}
</>
);
}
function () {
const = .();
return <>Local step: {....}</>;
}
function () {
const = .();
return (
<>
< ="button" ={() => ..()} ={..}>
Prev
</>
< ="button" ={() => ..()} ={..}>
Next
</>
</>
);
}
function () {
const = .();
return (
<>
{!.. && (
< ="button" ={() => ..()}>Back</>
)}
{.. ? (
< ="button" ={() => ..()}>Reset</>
) : (
< ="button" ={() => ..()}>Next</>
)}
</>
);
}
export function () {
return (
<.>
< />
< />
</.>
);
}Summary
- Scoped — Wrap your tree so any descendant can call
useStepper()and get the same stepper. For multi-scoped, nest a secondScopedinside the first so inner components can use both hooks. - useStepper() — Use in child components for
state.current,state.isFirst/state.isLast,flow.switch,navigation.next()/prev()/reset(). In multi-scoped, call the matching hook (GlobalStepper.useStepper()vsLocalStepper.useStepper()). - No primitives — Build your own step list, content panels, and buttons; Stepperize only provides state and navigation.
Additional resources
- Scoped API — Scoped component and useStepper when used with Scoped
- Hook API — useStepper state, navigation, flow, metadata
Last updated on