Navigation & Overlays

Popover Form

Animated popover form for React and shadcn/ui that expands from a button with a success state, for feedback and waitlists.

Updated

PopoverForm is a React component for shadcn/ui built with Motion layout animations that closes on click outside. Use it for feedback forms, waitlist signups, or quick contact inputs that should stay out of the way until needed.

References

Inspiration

Installation

pnpm dlx shadcn@latest add @cult-ui/popover-form

Usage

import { PopoverForm, PopoverFormButton, PopoverFormCutOutLeftIcon, PopoverFormCutOutRightIcon, PopoverFormSeparator, PopoverFormSuccess, } from "@/registry/default/ui/popover-form";
export default function PopoverFormFeedbackExample() { const [formState, setFormState] = useState<FormState>("idle"); const [open, setOpen] = useState(false); const [feedback, setFeedback] = useState(""); function submit() { setFormState("loading"); setTimeout(() => { setFormState("success"); }, 1500); setTimeout(() => { setOpen(false); setFormState("idle"); setFeedback(""); }, 3300); } useEffect(() => { const handleKeyDown = (event: KeyboardEvent) => { if (event.key === "Escape") { setOpen(false); } if ( (event.ctrlKey || event.metaKey) && event.key === "Enter" && open && formState === "idle" ) { submit(); } }; window.addEventListener("keydown", handleKeyDown); return () => window.removeEventListener("keydown", handleKeyDown); }, [open, formState]); return ( <div className="flex w-full items-center justify-center"> <PopoverForm title="Feedback" open={open} setOpen={setOpen} width="364px" height="192px" showCloseButton={formState !== "success"} showSuccess={formState === "success"} openChild={ <form onSubmit={(e) => { e.preventDefault(); if (!feedback) return; submit(); }} > <div className="relative"> <textarea autoFocus placeholder="Feedback" value={feedback} onChange={(e) => setFeedback(e.target.value)} className="h-32 w-full resize-none rounded-t-lg p-3 text-sm outline-none" required /> </div> <div className="relative flex h-12 items-center px-[10px]"> <PopoverFormSeparator /> <div className="absolute left-0 top-0 -translate-x-[1.5px] -translate-y-1/2"> <PopoverFormCutOutLeftIcon /> </div> <div className="absolute right-0 top-0 translate-x-[1.5px] -translate-y-1/2 rotate-180"> <PopoverFormCutOutRightIcon /> </div> <PopoverFormButton loading={formState === "loading"} /> </div> </form> } successChild={ <PopoverFormSuccess title="Feedback Received" description="Thank you for supporting our project!" /> } /> </div> ); }