Multi-Step Wizard
Step-by-step form wizard with validation, progress tracking, and review step.
Live Preview
- 1Account Personal info
- 2Preferences Your choices
- 3Review Confirm details
Key Features
- Stepper with labeled steps and completion states
- Per-step input validation before advancing
- Select menu for structured choices
- RadioGroup for single-choice selections
- Textarea for free-form input
- Review step with editable summary
- Progress bar tracking overall completion
Code
Multi-Step Wizard Recipe
<script lang="ts">
import {
Stepper, StepperStep, Input, Select, RadioGroup, RadioItem,
Textarea, Checkbox, Button, Card, Progress, Alert
} from '@urbicon-ui/blocks';
let step = $state(0);
let submitted = $state(false);
// Step 0: Account
let fullName = $state('');
let email = $state('');
// Step 1: Preferences
let plan = $state('');
let region = $state<string | null>(null);
// Step 2: Review
let notes = $state('');
let agreedToTerms = $state(false);
const regionOptions = [
{ label: 'United States', value: 'us' },
{ label: 'Europe', value: 'eu' },
{ label: 'Asia Pacific', value: 'asia' }
];
const planLabels: Record<string, string> = {
starter: 'Starter',
pro: 'Professional',
enterprise: 'Enterprise'
};
let progress = $derived(submitted ? 100 : Math.round((step / 3) * 100));
let canNext = $derived.by(() => {
if (step === 0) return fullName.trim() !== '' && email.trim() !== '';
if (step === 1) return plan !== '' && region !== null;
if (step === 2) return agreedToTerms;
return false;
});
function next() {
if (step < 2) step += 1;
else if (canNext) submitted = true;
}
function back() {
if (step > 0) step -= 1;
}
function reset() {
step = 0;
submitted = false;
fullName = '';
email = '';
plan = '';
region = null;
notes = '';
agreedToTerms = false;
}
</script>
<div class="mx-auto max-w-xl p-8">
<Progress value={progress} size="sm" intent="primary" class="mb-6" />
<Stepper bind:activeStep={step} orientation="horizontal">
<StepperStep label="Account" description="Personal info" />
<StepperStep label="Preferences" description="Your choices" />
<StepperStep label="Review" description="Confirm details" />
</Stepper>
{#if submitted}
<div class="mt-8">
<Alert intent="success" variant="soft" title="All done!">
Your wizard has been submitted successfully.
</Alert>
<Button variant="outlined" intent="neutral" onclick={reset} class="mt-4">
Start Over
</Button>
</div>
{:else}
<Card class="mt-8">
<div class="space-y-5 p-6">
{#if step === 0}
<Input label="Full Name" bind:value={fullName} required
placeholder="Jane Doe" />
<Input label="Email" type="email" bind:value={email} required
placeholder="jane@example.com" />
{:else if step === 1}
<RadioGroup bind:value={plan} label="Choose a plan">
<RadioItem value="starter" label="Starter"
description="For individuals and side projects" />
<RadioItem value="pro" label="Professional"
description="For growing teams" />
<RadioItem value="enterprise" label="Enterprise"
description="For large organizations" />
</RadioGroup>
<Select label="Region" options={regionOptions}
bind:value={region} placeholder="Select a region" />
{:else}
<Textarea label="Additional Notes" bind:value={notes}
autoResize showCounter maxlength={500}
placeholder="Anything else we should know?" />
<div class="bg-surface-subtle rounded-lg p-4">
<h4 class="text-text-primary mb-2 text-sm font-semibold">
Summary
</h4>
<dl class="text-text-secondary space-y-1 text-sm">
<div class="flex justify-between">
<dt>Name</dt><dd>{fullName}</dd>
</div>
<div class="flex justify-between">
<dt>Email</dt><dd>{email}</dd>
</div>
<div class="flex justify-between">
<dt>Plan</dt><dd>{planLabels[plan] ?? '—'}</dd>
</div>
<div class="flex justify-between">
<dt>Region</dt>
<dd>{regionOptions.find((o) => o.value === region)?.label ?? '—'}</dd>
</div>
</dl>
</div>
<Checkbox label="I agree to the terms and conditions"
bind:checked={agreedToTerms} />
{/if}
</div>
</Card>
<div class="mt-6 flex justify-between">
<Button variant="ghost" intent="neutral"
onclick={back} disabled={step === 0}>Back</Button>
<Button intent="primary" onclick={next}
disabled={!canNext}>
{step < 2 ? 'Next' : 'Submit'}
</Button>
</div>
{/if}
</div>