NRVM

Back to Discover
FormsIntermediate

Multi-Step Form

An elegant multi-step wizard with Framer Motion slide transitions.

Live Preview
1
2
3

Personal Info

"use client";
import { useState } from "react";
import { motion, AnimatePresence } from "framer-motion";

export function MultiStepForm() {
  const [step, setStep] = useState(1);
  
  return (
    <div className="w-full max-w-sm rounded-2xl border border-border bg-[#0a0a0a] p-8 shadow-xl">
      <div className="flex justify-between mb-8">
        {[1, 2, 3].map((i) => (
          <div key={i} className={`flex h-8 w-8 items-center justify-center rounded-full text-xs font-medium transition-colors ${step >= i ? "bg-foreground text-background" : "bg-muted text-muted-foreground"}`}>
            {i}
          </div>
        ))}
      </div>
      
      <div className="h-40 relative">
        <AnimatePresence mode="wait">
          {step === 1 && (
            <motion.div key="1" initial={{ opacity: 0, x: 20 }} animate={{ opacity: 1, x: 0 }} exit={{ opacity: 0, x: -20 }} className="absolute inset-0">
              <h3 className="text-lg font-medium mb-4">Personal Info</h3>
              <input type="text" placeholder="Full Name" className="w-full mb-3 rounded-md border border-border bg-transparent px-3 py-2 text-sm focus:outline-none focus:ring-1 focus:ring-foreground" />
            </motion.div>
          )}
          {step === 2 && (
            <motion.div key="2" initial={{ opacity: 0, x: 20 }} animate={{ opacity: 1, x: 0 }} exit={{ opacity: 0, x: -20 }} className="absolute inset-0">
              <h3 className="text-lg font-medium mb-4">Security</h3>
              <input type="password" placeholder="Password" className="w-full mb-3 rounded-md border border-border bg-transparent px-3 py-2 text-sm focus:outline-none focus:ring-1 focus:ring-foreground" />
            </motion.div>
          )}
          {step === 3 && (
            <motion.div key="3" initial={{ opacity: 0, x: 20 }} animate={{ opacity: 1, x: 0 }} exit={{ opacity: 0, x: -20 }} className="absolute inset-0">
              <h3 className="text-lg font-medium mb-4">Done!</h3>
              <p className="text-sm text-muted-foreground">Your account is ready to go.</p>
            </motion.div>
          )}
        </AnimatePresence>
      </div>
      
      <div className="flex justify-between mt-4">
        <button 
          onClick={() => setStep(s => Math.max(1, s - 1))}
          disabled={step === 1}
          className="text-sm font-medium text-muted-foreground disabled:opacity-50 hover:text-foreground"
        >
          Back
        </button>
        <button 
          onClick={() => setStep(s => Math.min(3, s + 1))}
          className="rounded-md bg-foreground text-background px-4 py-2 text-sm font-medium hover:bg-[#e0e0e0] transition-colors"
        >
          {step === 3 ? "Complete" : "Next"}
        </button>
      </div>
    </div>
  );
}