Multi-Step Flow
ericts-ui/multi-stepFreeComponent
A Motion-powered multi-step container that slides between steps and animates height changes.
Live preview
live · rendered by the registry
Rendered by ericts-ui on their own page — this is the component running, not a screenshot of it.Install it
npx shadcn@latest add https://ui.ericts.com/r/multi-step.jsonSource
1"use client";23import * as React from "react";4import {5 AnimatePresence,6 motion,7 MotionConfig,8 useReducedMotion,9 type HTMLMotionProps,10} from "motion/react";1112import { Button } from "@/components/ui/button";13import { useElementHeight } from "@/hooks/use-element-height";14import { cn } from "@/lib/utils";1516type MotionTransition = NonNullable<HTMLMotionProps<"div">["transition"]>;17type StepDirection = -1 | 1;1819export type MultiStepItem = {20 id: string;21 content: React.ReactNode;22};2324export type MultiStepProps = Omit<25 HTMLMotionProps<"div">,26 | "animate"27 | "children"28 | "defaultValue"29 | "initial"30 | "onChange"31 | "transition"32> & {33 steps: MultiStepItem[];34 currentStep?: number;35 defaultStep?: number;36 onStepChange?: (step: number) => void;37 backLabel?: React.ReactNode;38 continueLabel?: React.ReactNode;39 completeLabel?: React.ReactNode;40 disableBack?: boolean;41 disableContinue?: boolean;42 footer?: React.ReactNode;43 contentClassName?: string;44 innerClassName?: string;45 actionsClassName?: string;46 transition?: MotionTransition;47};4849export function MultiStep({50 steps,51 currentStep,52 defaultStep = 0,53 onStepChange,54 backLabel = "Back",55 continueLabel = "Continue",56 completeLabel = "Done",57 disableBack,58 disableContinue,59 footer,60 className,61 contentClassName,62 innerClassName,63 actionsClassName,64 transition,65 ...props66}: MultiStepProps) {67 const [uncontrolledStep, setUncontrolledStep] = React.useState(defaultStep);68 const [direction, setDirection] = React.useState<StepDirection>(1);69 const [innerRef, height] = useElementHeight<HTMLDivElement>();70 const shouldReduceMotion = useReducedMotion();7172 const selectedStep = clampStep(currentStep ?? uncontrolledStep, steps.length);73 const isControlled = currentStep !== undefined;74 const isFirstStep = selectedStep === 0;75 const isLastStep = selectedStep === steps.length - 1;76 const activeStep = steps[selectedStep];7778 const setStep = React.useCallback(79 (nextStep: number, nextDirection: StepDirection) => {80 const resolvedStep = clampStep(nextStep, steps.length);8182 setDirection(nextDirection);8384 if (!isControlled) {85 setUncontrolledStep(resolvedStep);86 }8788 onStepChange?.(resolvedStep);89 },90 [isControlled, onStepChange, steps.length],91 );9293 const resolvedTransition: MotionTransition = shouldReduceMotion94 ? { duration: 0 }95 : (transition ?? { type: "spring", duration: 0.5, bounce: 0 });96// … truncated
What it pulls in
npm packages
Other registry items
Files it writes
More from ericts-ui
All 33 items| Component | Registry | Kind | Access | Installs | Command |
|---|---|---|---|---|---|
| Adaptive Draweradaptive-drawer | ericts-ui | Components | Free | 2 deps | |
| Check Animationcheck-animation | ericts-ui | Components | Free | no deps · 2 files | |
| Context Cursorcontext-cursor | ericts-ui | Components | Free | 1 dep | |
| Copy Buttoncopy-button | ericts-ui | Components | Free | 2 deps | |
| Expandable Dialogexpandable-modal | ericts-ui | Components | Free | 1 dep | |
| Expandable Tabsexpandable-tabs | ericts-ui | Components | Free | 1 dep | |
| Expandable Toolbarexpandable-toolbar | ericts-ui | Components | Free | 1 dep | |
| Expanding Panelexpanding-panel | ericts-ui | Components | Free | 2 deps |
