Skip to content

Single Pane Stepper

The SinglePaneStepper is a single-column flow component for multi-step guided processes. It displays a vertical timeline on the left with collapsible cards on the right.

Use this component for linear onboarding flows, setup wizards, and configuration processes where each step follows sequentially.

Substep States

Substeps render distinct states in the timeline — completed (green check), skipped (arrow), and errored (red). A step whose failed substep is retried and completed shows as completed overall, with the failed substep still marked in the timeline. This example settles into a fully resolved flow on load.

Interactive Example

This example demonstrates a simple CI pipeline generation flow with step-based navigation.

Usage

import { SinglePaneStepper, useFlowCard } from "@harnessio/ui/components";
import type { FlowConfig } from "@harnessio/ui/components";
const flow: FlowConfig = {
steps: {
configure: { title: "Configure", description: "Set up your project" },
generate: { title: "Generate", description: "Create pipeline" },
},
subSteps: {
"choose-language": {
step: "configure",
component: LanguageCard,
next: "generate-pipeline",
},
"generate-pipeline": {
step: "generate",
component: GeneratePipelineCard,
},
},
initialSubStep: "choose-language",
};
<SinglePaneStepper.Root
flow={flow}
icon={<Logo />}
title="Generate CI Pipeline"
onComplete={(state) => console.log("Done!", state)}
/>

Anatomy

The Single Pane Stepper uses a compound component pattern:

<SinglePaneStepper.Root flow={flowConfig}>
{/* Cards are rendered automatically from flowConfig.subSteps */}
</SinglePaneStepper.Root>

Cards use the useFlowCard hook to interact with the flow engine:

function MyCard() {
const { state, status, complete, error, skip } = useFlowCard();
return (
<SinglePaneStepper.Card title="My Card">
{/* Card content */}
</SinglePaneStepper.Card>
);
}

Flow Configuration

The FlowConfig object defines the structure of your flow. It’s the same configuration format used by DualPaneStepper, making it easy to migrate between the two components.

interface FlowConfig {
steps: Record<string, StepConfig>; // Timeline steps
subSteps: Record<string, SubStepConfig>; // Card definitions
initialSubStep: string; // First card to show
}
interface StepConfig {
title: string;
description?: string;
}
interface SubStepConfig {
step: string; // Which step this substep belongs to
title: string; // Shown in the timeline
component: ComponentType; // React component to render as a card
next?: string; // Next substep ID (optional if dynamic)
}

API Reference

SinglePaneStepper.Root

PropTypeDescription
flowFlowConfigFlow configuration defining steps and substeps
iconReactNodeIcon displayed in the header
titlestringTitle displayed in the header
onComplete(state: Record<string, unknown>) => voidCallback when any card completes

SinglePaneStepper.Card

PropTypeDescription
titlestringCard title
descriptionstringCard description
childrenReactNodeCard content

useFlowCard Hook

Returns an object with the following properties:

PropertyTypeDescription
stateRecord<string, unknown>Accumulated flow state from all completed cards
status'pending' | 'active' | 'completed' | 'skipped' | 'error'Current card status
complete(data?: Record<string, unknown>, nextSubStep?: string) => voidMark card as complete and optionally navigate
error() => voidMark card as errored
skip(nextSubStep?: string) => voidSkip card and optionally navigate