

<Tabs items="[&#x22;Preview&#x22;, &#x22;Code&#x22;]">
  <Tab value="Preview">
    <Preview name="SingleStepperNavExample" />
  </Tab>

  <Tab value="Code">
    ```tsx
    "use client";

    import {
      Stepper,
      StepperItem,
      StepperTrigger,
      StepperIndicator,
      StepperTitle,
    } from "@tilt-legal/cubitt-components/primitives";
    import { z } from "zod";
    import { FormBuilder } from "@tilt-legal/cubitt-components/composites";

    const schema = z.object({
      client: z.object({
        name: z.string().min(1),
        email: z.string().email(),
      }),
      engagement: z.object({
        type: z.enum(["standard", "custom"]),
        startDate: z.date(),
      }),
      confirm: z.boolean().refine(Boolean),
    });

    type WizardValues = z.infer<typeof schema>;

    const formDefs = [
      {
        kind: "step",
        id: "client",
        title: "Client details",
        children: [/* fields */],
      },
      {
        kind: "step",
        id: "engagement",
        title: "Engagement",
        children: [/* fields */],
      },
      {
        kind: "step",
        id: "confirm",
        title: "Confirm",
        children: [/* fields */],
      },
    ] as const satisfies FormDefs<WizardValues>;

    export default function StepperNavForm() {
      const stepper = FormBuilder.Single.useStepper<WizardValues>(
        (slot) => slot.defaultStepper
      );

      const steps = formDefs.filter((n) => n.kind === "step");

      return (
        <div>
          <Stepper
            value={stepper.state.index}
            onValueChange={(idx) => stepper.actions.goTo(idx)}
            className="mb-8"
          >
            {steps.map((step, idx) => (
              <StepperItem
                key={step.id}
                step={idx}
                disabled={idx > stepper.state.index}
              >
                <StepperTrigger>
                  <StepperIndicator />
                  <StepperTitle>{step.title}</StepperTitle>
                </StepperTrigger>
              </StepperItem>
            ))}
          </Stepper>

          <FormBuilder.Single
            schema={schema}
            formDefs={formDefs}
            defaultValues={{...}}
            stepper={stepper.render}
            onSubmit={async (values) => console.log(values)}
          />
        </div>
      );
    }
    ```
  </Tab>
</Tabs>

## How It Works [#how-it-works]

1. **Keep default footer** – Pass `(slot) => slot.defaultStepper` to preserve Next/Back buttons
2. **Sync with Stepper** – Bind `value` to `stepper.state.index` and `onValueChange` to `stepper.actions.goTo`
3. **Disable future steps** – Prevent jumping ahead with `disabled={idx > stepper.state.index}`

The key difference from [Multi-Step Wizard](/composites/form-builder/single-examples/multi-step-wizard):

* `() => null` – Hides default UI for fully custom navigation
* `(slot) => slot.defaultStepper` – Keeps default footer, lets you add a visual progress indicator

<Callout type="info">
  See [Stepper Hook](/composites/form-builder/single#stepper-hook) for the full
  API and the [Stepper component](/primitives/stepper) for styling options.
</Callout>
