Skip to content

useStepState

A flexible React hook for managing step-by-step navigation state with support for custom properties and TypeScript type safety.

Installation

import { useStepState } from "@cfa/react-core";

Basic Usage

1. Simple Step Navigation

Pass an array of objects with label properties:

const steps = [
{ label: "Personal Information" },
{ label: "Account Setup" },
{ label: "Verification" },
{ label: "Complete" },
];
const {
steps: stepState,
activeStep,
incrementStep,
decrementStep,
goToStep,
} = useStepState(steps);
// stepState contains all steps with their current state
console.log(stepState);
// [
// { label: 'Personal Information', state: 'active', step: 1 },
// { label: 'Account Setup', state: 'inactive', step: 2 },
// { label: 'Verification', state: 'inactive', step: 3 },
// { label: 'Complete', state: 'inactive', step: 4 }
// ]
// activeStep is the currently active step
console.log(activeStep);
// { label: 'Personal Information', state: 'active', step: 1 }

2. Navigation Functions

const { activeStep, incrementStep, decrementStep, goToStep } =
useStepState(steps);
// Move to next step
incrementStep();
console.log(activeStep);
// { label: 'Account Setup', state: 'active', step: 2 }
// Previous step is now marked as complete
console.log(stepState[0]);
// { label: 'Personal Information', state: 'complete', step: 1 }
// Move back to previous step
decrementStep();
console.log(activeStep);
// { label: 'Personal Information', state: 'active', step: 1 }
// Next step is now marked as inactive
console.log(stepState[1]);
// { label: 'Account Setup', state: 'inactive', step: 2 }
// Go to a specific step
goToStep(3);
console.log(activeStep);
// { label: 'Verification', state: 'active', step: 3 }

Navigation Behavior:

  • incrementStep(): Moves forward, marks current step as “complete”
  • decrementStep(): Moves backward, marks current step as “inactive”
  • goToStep(stepNumber: number): Updates step to the passed in step
  • Automatically prevents going past first/last steps

3. Custom Properties

Add any custom properties to your step objects with full TypeScript support:

interface CustomStep {
label: string;
id: string;
description: string;
estimatedTime: number;
required: boolean;
metadata: {
category: "account" | "profile" | "verification";
priority: "high" | "medium" | "low";
};
}
const customSteps: CustomStep[] = [
{
label: "Create Account",
id: "create-account",
description: "Set up your basic account information",
estimatedTime: 3,
required: true,
metadata: { category: "account", priority: "high" },
},
{
label: "Complete Profile",
id: "complete-profile",
description: "Add your personal details",
estimatedTime: 5,
required: true,
metadata: { category: "profile", priority: "high" },
},
];
const { steps, activeStep } = useStepState(customSteps);
// All custom properties are preserved and accessible
console.log(activeStep?.id); // 'create-account'
console.log(activeStep?.estimatedTime); // 3
console.log(activeStep?.metadata.category); // 'account'
console.log(activeStep?.required); // true
// Custom properties remain on all steps
steps.forEach((step) => {
console.log(
`${step.label}: ${step.estimatedTime} min, ${step.metadata.priority} priority`
);
});

API Reference

Hook Signature

function useStepState<T extends BaseItem>(items: T[]): UseStepStateReturn<T>;

Return Value

{
steps: StepsState<T>[]; // All steps with current state
activeStep: StepsState<T> | undefined; // Currently active step
incrementStep: () => void; // Move to next step
decrementStep: () => void; // Move to previous step
goToStep: (stepNumber: number) => void // Move to passed in step
}

Step States

  • "active": Current step
  • "complete": Completed steps (behind active step)
  • "inactive": Future steps (ahead of active step)

Common Usage Patterns

Progress Tracking

const { steps } = useStepState(mySteps);
const completedSteps = steps.filter((step) => step.state === "complete").length;
const totalSteps = steps.length;
const progressPercentage = (completedSteps / totalSteps) * 100;

Conditional Navigation

const { activeStep, incrementStep } = useStepState(steps);
const handleNext = () => {
if (isValidStep(activeStep)) {
incrementStep();
}
};

Finding Specific Steps

const { steps } = useStepState(stepsWithIds);
const currentStepId = steps.find((step) => step.state === "active")?.id;
const completedStepIds = steps
.filter((step) => step.state === "complete")
.map((step) => step.id);