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>
);
}