MarketingNewsletter SignupNewsletter SignupA customizable newsletter signup form with validation and success message.Preview Subscribe to our newsletterStay up to date with our latest news and updates.Email addressSubscribe Code NewsletterSignup.tsx"use client"; import React, { useState } from "react"; import { motion, AnimatePresence } from "motion/react"; import { Send } from "lucide-react"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import confetti from "canvas-confetti"; export interface NewsletterSignupProps { onSubmit: (email: string) => Promise<void>; className?: string; } export const NewsletterSignup: React.FC<NewsletterSignupProps> = ({ onSubmit, className = "", }) => { const [email, setEmail] = useState(""); const [error, setError] = useState(""); const [isSubmitting, setIsSubmitting] = useState(false); const [isSubmitted, setIsSubmitted] = useState(false); const handleSubmit = async (e: React.FormEvent) => { e.preventDefault(); setError(""); if (!email) { setError("Email is required"); return; } const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; if (!emailRegex.test(email)) { setError("Please enter a valid email address"); return; } setIsSubmitting(true); try { await onSubmit(email); setIsSubmitted(true); confetti({ particleCount: 100, spread: 70, origin: { y: 0.6 }, }); } catch (err) { setError("An error occurred. Please try again."); } finally { setIsSubmitting(false); } }; return ( <div className={`bg-secondary/50 border border-primary/10 rounded-lg p-6 ${className}`} > <AnimatePresence mode="wait"> {!isSubmitted ? ( <motion.form key="form" initial={{ opacity: 0, y: 20 }} animate={{ opacity: 1, y: 0 }} exit={{ opacity: 0, y: -20 }} onSubmit={handleSubmit} className="space-y-4" > <div className="flex items-start justify-center gap-1 flex-col overflow-y-hidden"> <motion.h2 className="text-2xl font-bold text-foreground" initial={{ opacity: 0, y: 10 }} animate={{ opacity: 1, y: 0 }} transition={{ delay: 0.2 }} > Subscribe to our newsletter </motion.h2> <motion.p className="text-muted-foreground text-sm" initial={{ opacity: 0, y: 10, filter: "blur(3px)" }} animate={{ opacity: 1, y: 0, filter: "blur(0px)" }} transition={{ delay: 0.4 }} > Stay up to date with our latest news and updates. </motion.p> </div> <div className="space-y-2"> <motion.label initial={{ opacity: 0, filter: "blur(3px)" }} animate={{ opacity: 1, filter: "blur(0px)" }} transition={{ delay: 0.6 }} className="font-medium text-sm" htmlFor="email" > Email address </motion.label> <motion.div className="flex gap-2" initial={{ opacity: 0, filter: "blur(3px)" }} animate={{ opacity: 1, filter: "blur(0px)" }} transition={{ delay: 0.7 }} > <Input type="email" id="email" placeholder="you@example.com" value={email} onChange={(e) => setEmail(e.target.value)} className="w-full px-3 py-2 border rounded-md focus-visible:ring-0 focus-within:ring-0 focus:outline-white/10 " /> <Button type="submit" disabled={isSubmitting} className="relative overflow-hidden" > <motion.div key="default" initial={{ opacity: 1 }} exit={{ opacity: 0 }} className="flex items-center px-4" > <Send className="h-4 w-4" /> <span className="ml-2">Subscribe</span> </motion.div> </Button> </motion.div> </div> <AnimatePresence> {error && ( <motion.p className="text-red-500 text-sm" initial={{ opacity: 0, y: 5 }} animate={{ opacity: 1, y: 0 }} exit={{ opacity: 0, y: -5 }} > {error} </motion.p> )} </AnimatePresence> </motion.form> ) : ( <motion.div key="success" initial={{ opacity: 0, scale: 0.9 }} animate={{ opacity: 1, scale: 1 }} exit={{ opacity: 0 }} className="text-center" > <h2 className="text-2xl font-bold text-foreground mb-2"> Thank you for subscribing! </h2> <p className="text-muted-foreground"> We've sent a confirmation email to your inbox. </p> </motion.div> )} </AnimatePresence> </div> ); }; Usage import { NewsletterSignup } from "@/components/library/marketing/NewsletterSignup"; const MyComponent = () => { const handleSubmit = async (email: string) => { // Handle form submission await submitToAPI(email); }; return <NewsletterSignup onSubmit={handleSubmit} />; }; Props PropTypeDefaultonSubmit(email: string) => Promise<void>-classNamestring-Edit on GitHubLast updated on PreviousBrand ScrollerNextTestimonial Card