@doderasoftware/vue-stepper
v1.0.9
Published
A headless, accessible, and fully customizable multi-step stepper component for Vue 3. Build complex forms, wizards, and onboarding flows with ease. Features TypeScript support, responsive mobile design, step validation, optional steps, progress tracking,
Downloads
1,024
Maintainers
Readme
@doderasoftware/vue-stepper
A headless, accessible, and fully customizable multi-step stepper component for Vue 3. Build complex forms, wizards, and onboarding flows with ease.
📖 Documentation | 📦 npm | 🐛 Issues
✨ Features
- 🎨 Fully Customizable - Complete UI control via
:uiprop system - 🌙 Dark Mode - Built-in dark mode support (follows system preference)
- 📱 Responsive - Sidebar collapses on mobile with dropdown menu
- 🔒 Step Locking - Prevent navigation to specific steps
- ⏭️ Optional Steps - Mark steps as optional with visual indicators
- 📊 Progress Tracking - Automatic step counters and progress bars
- 🎯 TypeScript - Full type safety out of the box
- 🪶 Zero Dependencies - No external CSS frameworks required
- 🎛️ Headless Ready - Use your own styles or the built-in ones
📦 Installation
npm install @doderasoftware/vue-stepperThat's it! Styles are automatically included - no additional CSS imports needed.
🚀 Quick Start
<template>
<Stepper
v-model="currentStep"
:steps="steps"
:can-go-next="canProceed"
sidebar-title="Registration"
@complete="handleComplete"
>
<template #default="{ currentStep: stepIndex }">
<PersonalInfo v-if="stepIndex === 0" v-model="formData.personal" />
<AccountDetails v-else-if="stepIndex === 1" v-model="formData.account" />
<Review v-else-if="stepIndex === 2" :data="formData" />
</template>
</Stepper>
</template>
<script setup lang="ts">
import { ref, computed } from 'vue'
import { Stepper } from '@doderasoftware/vue-stepper'
const currentStep = ref(0)
const formData = ref({ personal: {}, account: {} })
const steps = [
{ category: 'Step 1', title: 'Personal Info', description: 'Enter your personal details' },
{ category: 'Step 2', title: 'Account Details', optional: true },
{ category: 'Step 3', title: 'Review & Submit' },
]
const canProceed = computed(() => {
if (currentStep.value === 0) {
return !!formData.value.personal.email
}
return true
})
function handleComplete() {
console.log('Form submitted:', formData.value)
}
</script>🌙 Dark Mode
The component includes full dark mode support out of the box. It uses class-based dark mode - add the dark class to your <html> or <body> element to enable dark styles.
<!-- Enable dark mode via class on html/body -->
<html class="dark">
...
</html>This approach is compatible with most dark mode implementations (Tailwind, manual toggle, etc.).
🎨 UI Customization
The :ui prop allows complete control over every element's styling. Pass class names to override the defaults:
<template>
<Stepper
v-model="currentStep"
:steps="steps"
:ui="{
root: 'my-stepper',
sidebar: 'bg-indigo-50 border-indigo-200',
navItemCurrent: 'bg-indigo-600 text-white',
indicatorCompleted: 'bg-indigo-600',
buttonPrimary: 'bg-gradient-to-r from-indigo-500 to-purple-600 hover:from-indigo-600 hover:to-purple-700',
progressFill: 'bg-indigo-600',
}"
>
<!-- your content -->
</Stepper>
</template>Available UI Keys
| Key | Description |
|-----|-------------|
| root | Root container |
| sidebar | Desktop sidebar container |
| sidebarContent | Sidebar inner content |
| sidebarTitle | Sidebar title text |
| nav | Navigation container |
| navItem | Step button - base |
| navItemCurrent | Step button - current |
| navItemCompleted | Step button - completed |
| navItemDisabled | Step button - disabled |
| indicator | Step indicator - base |
| indicatorCurrent | Step indicator - current |
| indicatorCompleted | Step indicator - completed |
| indicatorDisabled | Step indicator - disabled |
| stepCategory | Step category text |
| stepTitle | Step title text |
| progressBar | Progress bar track |
| progressFill | Progress bar fill |
| main | Main content area |
| card | Content card |
| navigation | Navigation buttons container |
| button | Button base |
| buttonPrimary | Primary button (next/complete) |
| buttonSecondary | Secondary button (back) |
| buttonDisabled | Disabled button state |
📋 Props
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| modelValue | number | 0 | Current step index (v-model) |
| steps | StepConfig[] | required | Array of step configurations |
| ui | StepperUI | {} | UI customization object |
| sidebarTitle | string | 'Steps' | Title displayed above the sidebar |
| canGoNext | boolean | true | Whether user can proceed to next step |
| nextButtonText | string | 'Continue' | Text for the next button |
| backButtonText | string | 'Back' | Text for the back button |
| completeButtonText | string | 'Complete' | Text for the final step button |
| showNavigation | boolean | true | Show/hide navigation buttons |
| showProgress | boolean | true | Show/hide progress indicator |
| showMobileHeader | boolean | true | Show/hide mobile header |
| showSidebar | boolean | true | Show/hide desktop sidebar |
| linear | boolean | true | Restrict navigation to sequential steps only |
📡 Events
| Event | Payload | Description |
|-------|---------|-------------|
| update:modelValue | number | Emitted when step index changes |
| step-change | { from: number, to: number } | Emitted on step navigation |
| next | StepChangeEvent | Emitted when next button is clicked |
| back | StepChangeEvent | Emitted when back button is clicked |
| complete | void | Emitted when completing the last step |
🎰 Slots
| Slot | Props | Description |
|------|-------|-------------|
| default | { currentStep, currentStepConfig, goNext, goBack, ... } | Main content area |
| header | - | Content above the step container |
| title | - | Title area inside step container |
| sidebar-header | - | Custom sidebar header |
| sidebar-footer | - | Custom sidebar footer |
| step-indicator | { step, index, isCurrent, isCompleted, isDisabled } | Custom step indicator |
| back-icon | - | Custom back button icon |
| next-icon | - | Custom next button icon |
📐 Step Configuration
interface StepConfig {
id?: string | number
category: string
title: string
disabled?: boolean
optional?: boolean
}🪝 Composable
Use the useMultiStep composable for headless control:
import { useMultiStep } from '@doderasoftware/vue-stepper'
const {
currentStep,
isFirstStep,
isLastStep,
progress,
goNext,
goBack,
goToStep,
isStepCompleted,
reset,
} = useMultiStep({
steps: mySteps,
initialStep: 0,
linear: true,
onStepChange: ({ from, to }) => console.log(`Step changed from ${from} to ${to}`),
})🔗 Links
📄 License
MIT © Dodera Software SRL
