Stepper pattern for linear progress with selectable steps, optional view-only step clicks, and previous/next controls.
Contact details
Capture the customer name, email, and phone number.
Shipping information
Collect shipping address and delivery preferences.
Payment method
Confirm card details and billing information.
Alpine.js Stepper
huxStepper manages active step state for linear or direct-navigation flows. Use it as a base primitive for Wizard-style experiences where each stage has its own content, validation, and completion rules.
Runtime Constraints
No special browser or runtime constraints beyond Alpine.js initialization.
API
huxStepper(config)
Returns an Alpine data object with:
activeStepId: string | nullstepItems: Array<{ label: string, id: string }>stepperId: string | nullstepsAreViewable: booleanactiveStepIndex: numberisFirstStep: booleanisLastStep: booleangoToStep(stepId: string): voidgoToPreviousStep(): voidgoToNextStep(): voidisStepComplete(stepId: string): booleanisStepCurrent(stepId: string): boolean
Internal helper methods are private implementation details and are not part of the supported API contract.
Options
stepItems: Array<{ label: string, id: string }>(default:[])activeStep: string(optional initial active step id)id: string(optional; enableshux-stepper:{id}:changeevents)stepsAreViewable: boolean(default:false; whentrue, clicking steps only changes what is viewed andisStepComplete()does not infer completion)
Quick Start
<div
x-data="huxStepper({
stepItems: [
{ label: 'Contact', id: 'contact' },
{ label: 'Shipping', id: 'shipping' },
{ label: 'Payment', id: 'payment' }
]
})"
>
<ol aria-label="Checkout steps">
<template x-for="stepItem in stepItems" x-bind:key="stepItem.id">
<li>
<button
type="button"
x-bind:id="`step-${stepItem.id}`"
x-bind:aria-controls="`step-panel-${stepItem.id}`"
x-bind:aria-current="isStepCurrent(stepItem.id) ? 'step' : null"
x-on:click="goToStep(stepItem.id)"
x-text="stepItem.label"
></button>
</li>
</template>
</ol>
<section
id="step-panel-contact"
role="region"
aria-labelledby="step-contact"
aria-live="polite"
x-show="activeStepId === 'contact'"
x-cloak
>
...
</section>
<button type="button" x-bind:disabled="isFirstStep" x-on:click="goToPreviousStep()">
Previous
</button>
<button type="button" x-bind:disabled="isLastStep" x-on:click="goToNextStep()">Next</button>
</div>Common Usage Patterns
Set an Explicit Initial Step
huxStepper({
stepsAreViewable: true,
activeStep: 'shipping',
stepItems: [
{ label: 'Contact', id: 'contact' },
{ label: 'Shipping', id: 'shipping' },
{ label: 'Payment', id: 'payment' },
],
})Subscribe to Change Events for Decoupled Composition
Use id to emit changes and coordinate external UI or validation logic.
huxStepper({
id: 'checkout',
stepItems: [
{ label: 'Contact', id: 'contact' },
{ label: 'Shipping', id: 'shipping' },
{ label: 'Payment', id: 'payment' },
],
})window.addEventListener('hux-stepper:checkout:change', (event) => {
const { stepId, stepIndex, totalSteps } = event.detail
console.log(stepId, stepIndex, totalSteps)
})Behavior Contract
- Invalid step items are filtered during initialization.
- If
activeStepis missing or invalid, active step falls back to the first valid step id. goToStep()ignores unknown ids.goToPreviousStep()andgoToNextStep()no-op when already at boundaries.isStepComplete()returnstrueonly for steps before the active step whenstepsAreViewableisfalse.- When
stepsAreViewableistrue,goToStep()is view-only andisStepComplete()always returnsfalse. - When
idis set,hux-stepper:{id}:changeis dispatched on init and on every successful step change.
Error Handling
- Invalid
stepItemsentries are ignored without throwing. - Unknown step ids no-op in
goToStep()and completion/current helpers. - Empty step arrays initialize with
activeStepId = null,isLastStep = true, and boundary navigation no-ops. - With
stepsAreViewable: true, completion is never inferred from navigation state.
Accessibility Notes
- Use semantic step navigation such as
<ol>with clear step labels. - Use
aria-current="step"on the active step trigger. - Keep controls as
button type="button"and provide visible text labels. - Keep active panel content announced with
aria-live="polite"when the content swap is meaningful for assistive technology users.
Notes
- Event payload when
idis set:{ stepId: string, stepIndex: number, totalSteps: number }. huxStepperis designed to be composed with form validation, conditional logic, and route/state persistence when building a full Wizard flow.