/Components/Application
Waitlist Form
Animated and minimal waitlist form with email input and submit button.
Preview
Join the waitlist for theHextaUI
Installation
"use client";
import * as React from "react";
import { useState, useEffect, useCallback } from "react";
import { motion, AnimatePresence } from "framer-motion";
declare module "canvas-confetti";
// Constants
const SMALL_BREAKPOINT = 570;
const containerVariants = {
hidden: { opacity: 0 },
visible: { opacity: 1, transition: { staggerChildren: 0.2 } },
};
const childVariants = {
hidden: { opacity: 0, filter: "blur(10px)" },
visible: {
opacity: 1,
filter: "blur(0px)",
transition: { duration: 0.5 },
},
};
// Hook to track window width
function useWindowWidth() {
const [width, setWidth] = useState(0);
useEffect(() => {
setWidth(window.innerWidth);
const handleResize = () => setWidth(window.innerWidth);
window.addEventListener("resize", handleResize);
return () => window.removeEventListener("resize", handleResize);
}, []);
return width;
}
const WaitlistForm = () => {
const [email, setEmail] = useState("");
const [submitted, setSubmitted] = useState(false);
const width = useWindowWidth();
const handleSubmit = useCallback((e: React.FormEvent) => {
e.preventDefault();
setSubmitted(true);
import("canvas-confetti").then((confetti) => {
confetti.default({
particleCount: 100,
spread: 70,
origin: { y: 0.6 },
});
});
}, []);
const ThankYouMessage = (
<motion.div
layout
initial={{ opacity: 0, filter: "blur(10px)" }}
animate={{
opacity: 1,
filter: "blur(0px)",
transition: { duration: 0.5 },
}}
exit={{
opacity: 0,
filter: "blur(10px)",
transition: { duration: 0.5 },
}}
>
<div className="flex flex-col items-center justify-center text-center">
<h1 className="text-4xl font-medium bg-gradient-to-b from-[#00000030] to-[#000] bg-clip-text text-transparent">
Thank you for joining the waitlist!
</h1>
<p className="mt-4 text-lg text-gray-700">
We will notify you with updates soon.
</p>
</div>
</motion.div>
);
const FormContent = (
<motion.div
layout
key="form"
className="w-full"
initial={{ opacity: 0, filter: "blur(10px)" }}
animate={{
opacity: 1,
filter: "blur(0px)",
transition: { duration: 0.5 },
}}
exit={{
opacity: 0,
filter: "blur(10px)",
transition: { duration: 0.5 },
}}
>
<motion.div
className={` ${
width <= SMALL_BREAKPOINT ? "text-left mb-4" : "text-center mb-8"
}`}
variants={childVariants}
>
<h1 className="text-4xl font-medium bg-gradient-to-b from-[#00000030] to-[#000] bg-clip-text text-transparent">
Join the waitlist for the
<span
className={`pl-2 ${
width <= SMALL_BREAKPOINT ? "" : "block"
} bg-gradient-to-r from-[#ff9292] to-[#ff0000] bg-clip-text text-transparent`}
>
HextaUI
</span>
</h1>
</motion.div>
<motion.form
onSubmit={handleSubmit}
className={
width <= SMALL_BREAKPOINT
? "flex flex-col gap-4 items-start w-full"
: "relative w-full "
}
variants={childVariants}
>
<div
className={
width <= SMALL_BREAKPOINT
? "flex flex-col gap-4 w-full"
: "flex flex-row items-center w-full space-x-4 rounded-full border border-gray-200 bg-white p-2"
}
>
<input
type="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
placeholder="name@example.com"
className={`${
width <= SMALL_BREAKPOINT
? "px-4 py-4 w-full text-lg focus:outline-none rounded-full border border-gray-200 bg-white"
: "flex-1 rounded-full bg-transparent px-4 py-4 text-lg focus:outline-none"
}`}
required
/>
<button
type="submit"
className={`${
width <= SMALL_BREAKPOINT
? "rounded-full bg-[#3a3aff] px-6 py-4 text-lg font-medium text-white hover:bg-[#4a2aff] transition w-full"
: "rounded-full bg-[#3a3aff] px-6 py-4 text-lg font-medium text-white hover:bg-[#4a2aff] transition"
}`}
>
Join the waitlist
</button>
</div>
</motion.form>
</motion.div>
);
return (
<motion.div
layout
className={`w-[95%] max-w-2xl rounded-4xl bg-white border border-black/10 shadow-2xl/10 ${
width <= SMALL_BREAKPOINT ? "p-8" : "p-12"
} min-h-[300px] flex items-center`}
variants={containerVariants}
initial="hidden"
animate="visible"
>
<AnimatePresence mode="wait">
{!submitted ? FormContent : ThankYouMessage}
</AnimatePresence>
</motion.div>
);
};
export { WaitlistForm };
Usage
import { WaitlistForm } from "@/components/library/WaitlistForm";
export default function App() {
return (
<div className="flex items-center justify-center min-h-screen bg-gray-100">
<WaitlistForm />
</div>
);
}
Edit on GitHub
Last updated on