← Components/Forms

MultiStepForm

multistepform-1779378816985.tsx
'use client'
import { useState } from 'react'

const STEPS = [
  { title: 'Account', fields: ['name', 'email'] },
  { title: 'Profile', fields: ['role', 'company'] },
  { title: 'Confirm', fields: [] },
]

export default function MultiStepForm() {
  const [step, setStep] = useState(0)
  const [data, setData] = useState({ name: '', email: '', role: '', company: '' })
  const [done, setDone] = useState(false)

  const set = (k: string) => (e: React.ChangeEvent<HTMLInputElement | HTMLSelectElement>) =>
    setData(d => ({ ...d, [k]: e.target.value }))

  const canNext = step === 0 ? data.name && data.email : step === 1 ? data.role : true

  if (done) return (
    <div style={{ padding: 40, background: '#0A0A0A', display: 'flex', alignItems: 'center', justifyContent: 'center', minHeight: 400 }}>
      <div style={{ textAlign: 'center' }}>
        <div style={{ fontSize: 56, marginBottom: 16 }}>🎉</div>
        <div style={{ color: '#F5F5F0', fontSize: 22, fontWeight: 700, marginBottom: 8 }}>Account Created!</div>
        <div style={{ color: 'rgba(255,255,255,0.5)', fontSize: 14 }}>Welcome, {data.name}</div>
        <button onClick={() => { setDone(false); setStep(0); setData({ name:'',email:'',role:'',company:'' }) }}
          style={{ marginTop: 24, background: '#C9A84C', color: '#0A0A0A', border: 'none', borderRadius: 8, padding: '10px 24px', fontWeight: 700, cursor: 'pointer', fontSize: 14 }}>
          Start over
        </button>
      </div>
    </div>
  )

  return (
    <div style={{ padding: 40, background: '#0A0A0A', display: 'flex', alignItems: 'center', justifyContent: 'center', minHeight: 400 }}>
      <div style={{ width: 380 }}>
        {/* Progress */}
        <div style={{ display: 'flex', gap: 8, marginBottom: 32, alignItems: 'center' }}>
          {STEPS.map((s, i) => (
            <div key={i} style={{ display: 'flex', alignItems: 'center', flex: i < STEPS.length - 1 ? 1 : 'none' }}>
              <div style={{
                width: 28, height: 28, borderRadius: '50%', flexShrink: 0,
                background: i < step ? '#22c55e' : i === step ? '#C9A84C' : 'rgba(255,255,255,0.1)',
                color: i <= step ? '#0A0A0A' : 'rgba(255,255,255,0.4)',
                display: 'flex', alignItems: 'center', justifyContent: 'center', fontSize: 12, fontWeight: 700,
                transition: 'all 0.3s',
              }}>{i < step ? '✓' : i + 1}</div>
              {i < STEPS.length - 1 && (
                <div style={{ flex: 1, height: 2, margin: '0 8px', background: i < step ? '#22c55e' : 'rgba(255,255,255,0.08)', transition: 'background 0.3s' }} />
              )}
            </div>
          ))}
        </div>

        <div style={{ color: '#F5F5F0', fontSize: 20, fontWeight: 700, marginBottom: 8 }}>{STEPS[step].title}</div>
        <div style={{ color: 'rgba(255,255,255,0.4)', fontSize: 13, marginBottom: 24 }}>Step {step + 1} of {STEPS.length}</div>

        {/* Fields */}
        <div style={{ display: 'flex', flexDirection: 'column', gap: 16, marginBottom: 32 }}>
          {step === 0 && <>
            <input placeholder="Full name *" value={data.name} onChange={set('name')} style={inp} />
            <input placeholder="Email address *" value={data.email} onChange={set('email')} style={inp} />
          </>}
          {step === 1 && <>
            <select value={data.role} onChange={set('role')} style={inp}>
              <option value="">Select your role *</option>
              {['Developer', 'Designer', 'Product Manager', 'Founder', 'Other'].map(r => <option key={r}>{r}</option>)}
            </select>
            <input placeholder="Company (optional)" value={data.company} onChange={set('company')} style={inp} />
          </>}
          {step === 2 && (
            <div style={{ background: 'rgba(255,255,255,0.03)', border: '1px solid rgba(255,255,255,0.08)', borderRadius: 10, padding: 20 }}>
              {[['Name', data.name], ['Email', data.email], ['Role', data.role], ['Company', data.company || '—']].map(([k,v]) => (
                <div key={k} style={{ display: 'flex', justifyContent: 'space-between', padding: '8px 0', borderBottom: '1px solid rgba(255,255,255,0.04)' }}>
                  <span style={{ color: 'rgba(255,255,255,0.4)', fontSize: 13 }}>{k}</span>
                  <span style={{ color: '#F5F5F0', fontSize: 13, fontWeight: 500 }}>{v}</span>
                </div>
              ))}
            </div>
          )}
        </div>

        <div style={{ display: 'flex', gap: 12 }}>
          {step > 0 && <button onClick={() => setStep(s => s - 1)} style={{ flex: 1, background: 'rgba(255,255,255,0.06)', color: '#F5F5F0', border: '1px solid rgba(255,255,255,0.1)', borderRadius: 8, padding: '12px', cursor: 'pointer', fontSize: 14, fontWeight: 600 }}>Back</button>}
          <button onClick={() => step < STEPS.length - 1 ? setStep(s => s + 1) : setDone(true)} disabled={!canNext}
            style={{ flex: 1, background: canNext ? '#C9A84C' : 'rgba(255,255,255,0.08)', color: canNext ? '#0A0A0A' : 'rgba(255,255,255,0.3)', border: 'none', borderRadius: 8, padding: '12px', cursor: canNext ? 'pointer' : 'default', fontSize: 14, fontWeight: 700 }}>
            {step === STEPS.length - 1 ? 'Create account' : 'Continue'}
          </button>
        </div>
      </div>
    </div>
  )
}

const inp: React.CSSProperties = {
  background: 'rgba(255,255,255,0.04)', border: '1.5px solid rgba(255,255,255,0.1)',
  borderRadius: 10, color: '#F5F5F0', fontSize: 14, padding: '12px 16px', outline: 'none', width: '100%', boxSizing: 'border-box'
}

Component info

CategoryForms
Frameworkreact
TierFREE
Views0
Copies0

About

Multi-step wizard form with progress bar, validation per step, and animated transitions

More from Forms

'use client'
import { useState } from 'react'

interface FloatingLabelInputProps {
  label?: string
  type?: string
  maxLength?: number
  required?: boolean
  pattern?: string
  helperText?: string
}

export default function FloatingLabelInput({ lab
FloatingLabelInput
Forms