An open API service indexing awesome lists of open source software.

https://github.com/archcorsair/ink-stepper

Step-by-step wizard component for Ink terminal applications.
https://github.com/archcorsair/ink-stepper

ink step-by-step stepper terminal tui wizard

Last synced: 5 months ago
JSON representation

Step-by-step wizard component for Ink terminal applications.

Awesome Lists containing this project

README

          

# ink-stepper

Step-by-step wizard component for [Ink](https://github.com/vadimdemedes/ink) terminal applications.

```
━━━━━ ✓ ━━━━━━━━━━ ✓ ━━━━━━━━━━●━━━━━━━━━━○━━━━━
Theme Directory Review Done

┌─────────────────────────────────────────────────┐
│ │
│ Review your selections: │
│ │
│ Theme: Dark │
│ Directory: ~/projects │
│ │
│ Press Enter to continue, Escape to go back │
│ │
└─────────────────────────────────────────────────┘
```

## Installation

```bash
# npm
npm install ink-stepper

# jsr
npx jsr add @archcorsair/ink-stepper

# pnpm
pnpm add ink-stepper

# bun
bun add ink-stepper
```

## Usage

Full documentation available here: https://archcorsair.github.io/ink-stepper/

```tsx
import { Stepper, Step } from "ink-stepper";
import { Text } from "ink";

function App() {
return (
process.exit(0)} onCancel={() => process.exit(1)}>




{({ goNext, goBack }) => (

)}


{({ goBack, isLast }) => (

)}


);
}
```

## API

### ``

Main container component that orchestrates step navigation.

| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `children` | `ReactNode` | required | Step elements |
| `onComplete` | `() => void` | required | Called when advancing past the last step |
| `onCancel` | `() => void` | - | Called when canceling (Escape on first step or `cancel()`) |
| `onStepChange` | `(step: number) => void` | - | Called when current step changes (zero-based index) |
| `onEnterStep` | `(step: number) => void` | - | Called after entering a step |
| `onExitStep` | `(step: number) => void \| boolean \| Promise` | - | Called before leaving a step (return `false` to cancel) |
| `step` | `number` | - | Controlled step index (zero-based) |
| `keyboardNav` | `boolean` | `true` | Enable Enter/Escape navigation |
| `showProgress` | `boolean` | `true` | Show the progress bar |
| `renderProgress` | `(ctx: ProgressContext) => ReactNode` | - | Custom progress bar renderer |
| `markers` | `StepperMarkers` | - | Custom progress bar markers |

### ``

Marker component for defining individual steps.

| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `name` | `string` | required | Display name in progress bar |
| `canProceed` | `boolean \| (() => boolean \| Promise)` | `true` | Whether navigation to next step is allowed (supports async) |
| `children` | `ReactNode \| (ctx: StepContext) => ReactNode` | required | Step content |

### StepContext

Context passed to step content when using the render function pattern:

```tsx
interface StepContext {
goNext: () => void; // Navigate to next step (respects canProceed)
goBack: () => void; // Navigate to previous step
goTo: (step: number) => void; // Jump to specific step (zero-based)
cancel: () => void; // Cancel the wizard
currentStep: number; // Current step index (zero-based)
totalSteps: number; // Total number of steps
isFirst: boolean; // Whether this is the first step
isLast: boolean; // Whether this is the last step
isValidating: boolean; // Whether async validation is in progress
}
```

### ProgressContext

Context passed to custom progress bar renderer:

```tsx
interface ProgressContext {
currentStep: number;
steps: Array<{
name: string;
completed: boolean;
current: boolean;
}>;
}
```

## Keyboard Navigation

By default, keyboard navigation is enabled:
- **Enter** - Advance to next step (if `canProceed` is true)
- **Escape** - Go back (or cancel if on first step)

Disable with `keyboardNav={false}`.

## Validation

### Synchronous Validation

Control navigation with the `canProceed` prop:

```tsx
function App() {
const [isValid, setIsValid] = useState(false);

return (


{({ goNext }) => (
setIsValid(value.length > 0)}
onSubmit={goNext}
/>
)}


);
}
```

### Async Validation

`canProceed` supports async functions for server-side validation:

```tsx
function App() {
const validateEmail = async () => {
const response = await fetch(`/api/validate?email=${email}`);
return response.ok;
};

return (


{({ goNext, isValidating }) => (


{isValidating && Validating...}

Continue


)}


);
}
```

The `isValidating` flag in StepContext is `true` while async validation is running, allowing you to show loading states.

## Lifecycle Hooks

### onEnterStep / onExitStep

Execute logic when entering or leaving steps:

```tsx
{
analytics.track(`entered_step_${step}`);
}}
onExitStep={async (step) => {
// Save draft before leaving
await saveDraft(step);
return true; // Allow navigation
}}
>
...

```

`onExitStep` can return `false` (sync or async) to cancel navigation:

```tsx
{
if (hasUnsavedChanges) {
return confirm("Discard changes?");
}
return true;
}}
>
...

```

## Input Coordination

When steps contain interactive inputs (TextInput, Select, etc.), use `useStepperInput` to prevent keyboard conflicts:

```tsx
import { useStepperInput } from "ink-stepper";

function EmailInput() {
const { disableNavigation, enableNavigation } = useStepperInput();
const [value, setValue] = useState("");

return (

);
}
```

This prevents Enter from advancing the step while the user is typing.

## Controlled Mode

For external state management, use the `step` prop:

```tsx
function App() {
const [currentStep, setCurrentStep] = useState(0);

return (

...
...

);
}
```

## Wrapped & Nested Steps

Steps can be wrapped in custom components, fragments, or conditional logic:

```tsx
const StepGroup = ({ children }) => <>{children}>;



This works!


{showOptional && (

Conditional step

)}

```

## Custom Progress Bar

### Custom Markers

Customize the progress bar markers without replacing the entire component:

```tsx
]", pending: "[ ]" }}
>
...

```

Default markers: `✓` (completed), `●` (current), `○` (pending)

### Custom Renderer

Full control over progress bar rendering:

```tsx
(

Step {currentStep + 1} of {steps.length}: {steps[currentStep].name}

)}
>
...

```

## Advanced: useStepperContext

For advanced use cases, access the full stepper context:

```tsx
import { useStepperContext } from "ink-stepper";

function CustomStepContent() {
const { stepContext, currentStepId } = useStepperContext();

return (

Step {stepContext.currentStep + 1}
Next

);
}
```

## Exports

```tsx
// Components
export { Stepper, Step } from "ink-stepper";

// Hooks
export { useStepperContext, useStepperInput } from "ink-stepper";

// Types
export type {
StepperProps,
StepProps,
StepContext,
ProgressContext,
StepperMarkers,
StepperContextValue,
RegisteredStep,
UseStepperInputReturn,
} from "ink-stepper";
```

## License

MIT