# useFormStepsInfo

URL: https://docs.forms.saastro.io/docs/use-form-steps-info
> Step metadata for building progress bars, tab navigators, and step indicators.

# useFormStepsInfo

Computes rich metadata for every step — status, error counts, completion, progress percentage. Designed to power UI components like progress bars, tab navigators, and step indicators.

> If you don't want to build your own UI, the package ships three prebuilt components on top of this hook: `StepsProgress`, `StepsNavigation`, and `StepsAccordion` — all take the same `steps`, `currentStepId`, `stepHistory`, and `fields` props. (`StepsAccordion` additionally requires Accordion components in your component registry — see [Components](/docs/components).)

---

## Signature

```tsx

const info = useFormStepsInfo({ steps, currentStepId, stepHistory, fields });
```

> Must be called inside a react-hook-form `FormProvider` (it reads error state via `useFormContext()`). Anywhere inside `<Form>`'s children this is automatic. If you drive [`useFormState`](/docs/use-form-state) yourself, wrap your tree in `<FormProvider {...methods}>` (from `react-hook-form`) first.

---

## Parameters

| Parameter       | Type       | Description               |
| --------------- | ---------- | ------------------------- |
| `steps`         | `Steps`    | Steps configuration map (`Record<string, Step>`) |
| `currentStepId` | `string`   | Active step ID            |
| `stepHistory`   | `string[]` | Stack of visited step IDs (excludes the current step) |
| `fields`        | `Fields`   | Field definitions map (`Record<string, FieldConfig>`) |

These values typically come from [`useFormState`](/docs/use-form-state): `currentStepId` and `stepHistory` are returned directly, and `steps`/`fields` are read from the resolved `config` it returns (`config.steps`, `config.fields`).

---

## Return Value: FormStepsInfo

| Property            | Type                     | Description                            |
| ------------------- | ------------------------ | -------------------------------------- |
| `steps`             | `StepInfo[]`             | Metadata for every step (see below)    |
| `currentStepId`     | `string`                 | Active step ID                         |
| `totalSteps`        | `number`                 | Total number of steps                  |
| `completedSteps`    | `number`                 | Count of completed steps               |
| `progress`          | `number`                 | Completion percentage (0-100)          |
| `stepHistory`       | `string[]`               | Stack of visited step IDs              |
| `canGoNext`         | `boolean`                | Heuristic: `true` when there is step history or more than one step (it does not check whether a next step actually exists from the current one) |
| `canGoPrev`         | `boolean`                | Whether back navigation is possible (`stepHistory` is non-empty) |
| `getStepInfo(id)`   | `(id: string) => StepInfo \| undefined` | Get info for a specific step (`undefined` for unknown IDs) |
| `getStepStatus(id)` | `(id: string) => StepStatus` | Get status for a specific step (`'pending'` for unknown IDs) |

### StepInfo

| Property      | Type         | Description                                           |
| ------------- | ------------ | ----------------------------------------------------- |
| `id`          | `string`     | Step ID                                               |
| `fields`      | `string[]`   | Field names in this step                              |
| `fieldCount`  | `number`     | Number of fields                                      |
| `status`      | `StepStatus` | `'pending'`, `'current'`, `'completed'`, or `'error'` |
| `hasErrors`   | `boolean`    | Whether any fields have validation errors             |
| `errorCount`  | `number`     | Number of fields with errors                          |
| `isVisited`   | `boolean`    | Whether the step has been visited                     |
| `isCurrent`   | `boolean`    | Whether this is the active step                       |
| `isCompleted` | `boolean`    | Visited, not current, no errors, all fields valid     |
| `canNavigate` | `boolean`    | Whether the user can navigate to this step            |

### StepStatus

```tsx
type StepStatus = 'pending' | 'current' | 'completed' | 'error';
```

- `'pending'` — Not yet visited
- `'current'` — Active step with no validation errors (an active step with errors reports `'error'`)
- `'completed'` — Visited, not current, no errors, all fields valid
- `'error'` — Has validation errors (current or visited)

> **Reactivity note:** the result is memoized and reads field values non-reactively (via `getValues()`). It refreshes when the current step, the step history, or validation errors change — not on every keystroke.

---

## Example: Custom Progress Bar

```tsx

interface StepProgressProps {
  steps: Steps;
  currentStepId: string;
  stepHistory: string[];
  fields: Fields;
}

function StepProgress({ steps, currentStepId, stepHistory, fields }: StepProgressProps) {
  const info = useFormStepsInfo({ steps, currentStepId, stepHistory, fields });

  return (
    <div>
      <div className="flex gap-2 mb-4">
        {info.steps.map((step) => (
          <div
            key={step.id}
            className={`flex-1 h-2 rounded ${
              step.isCompleted
                ? 'bg-green-500'
                : step.isCurrent
                  ? 'bg-blue-500'
                  : step.hasErrors
                    ? 'bg-red-500'
                    : 'bg-gray-200'
            }`}
          />
        ))}
      </div>
      <p className="text-sm text-muted-foreground">
        Step {info.steps.findIndex((s) => s.isCurrent) + 1} of {info.totalSteps} ({info.progress}%
        complete)
      </p>
    </div>
  );
}
```

## Example: Step Navigator with Error Badges

```tsx

interface StepNavProps {
  steps: Steps;
  currentStepId: string;
  stepHistory: string[];
  fields: Fields;
  onStepClick: (stepId: string) => void;
}

function StepNav({ steps, currentStepId, stepHistory, fields, onStepClick }: StepNavProps) {
  const info = useFormStepsInfo({ steps, currentStepId, stepHistory, fields });

  return (
    <nav className="flex gap-1">
      {info.steps.map((step, i) => (
        <button
          key={step.id}
          onClick={() => step.canNavigate && onStepClick(step.id)}
          disabled={!step.canNavigate}
          className={`px-3 py-1 rounded text-sm ${
            step.isCurrent
              ? 'bg-primary text-white'
              : step.isCompleted
                ? 'bg-green-100 text-green-800'
                : 'bg-gray-100 text-gray-500'
          }`}
        >
          {i + 1}. {step.id}
          {step.hasErrors && <span className="ml-1 text-xs text-red-500">({step.errorCount})</span>}
        </button>
      ))}
    </nav>
  );
}
```

---

## Related

- [Multi-Step Forms](/docs/multi-step) — Full guide on step configuration, branching, and navigation
- [useFormState](/docs/use-form-state) — The form engine that provides `currentStepId`, `stepHistory`, and the resolved config
