modals /
Newsletter Popup
Email capture popup that auto-triggers after 2 seconds. Includes a success state and social proof.
Preview
Source
tsx
"use client";
import { useState, useEffect } from "react";
import {
Dialog,
DialogContent,
DialogHeader,
DialogTitle,
DialogDescription,
} from "@/components/ui/dialog";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Sparkles, ArrowRight } from "lucide-react";
export default function NewsletterPopup() {
const [open, setOpen] = useState(false);
const [email, setEmail] = useState("");
const [submitted, setSubmitted] = useState(false);
// Auto-show after 2 seconds (SSR-guarded)
useEffect(() => {
const timer = setTimeout(() => setOpen(true), 2000);
return () => clearTimeout(timer);
}, []);
function handleSubmit(e: React.FormEvent) {
e.preventDefault();
if (!email) return;
setSubmitted(true);
}
return (
<div className="flex min-h-[200px] items-center justify-center p-8">
{/* Demo trigger */}
<Button variant="outline" onClick={() => setOpen(true)}>
Preview popup
</Button>
<Dialog open={open} onOpenChange={setOpen}>
<DialogContent className="sm:max-w-md overflow-hidden p-0">
{/* Top accent bar */}
<div className="h-1 w-full bg-gradient-to-r from-violet-500 via-purple-500 to-fuchsia-500" />
<div className="p-6 sm:p-8">
{submitted ? (
<div className="py-8 text-center space-y-3">
<div className="mx-auto flex h-14 w-14 items-center justify-center rounded-full bg-green-100 text-green-600 text-2xl">
✓
</div>
<h3 className="text-xl font-bold text-foreground">You're in!</h3>
<p className="text-muted-foreground text-sm">
Your free guide is on its way to <strong>{email}</strong>.
Check your inbox in the next few minutes.
</p>
<Button className="mt-2" onClick={() => setOpen(false)}>
Got it
</Button>
</div>
) : (
<>
<DialogHeader className="text-left mb-5">
<div className="flex items-center gap-2 mb-3">
<div className="rounded-full bg-violet-100 p-2 text-violet-600">
<Sparkles className="h-4 w-4" />
</div>
<span className="text-xs font-semibold uppercase tracking-widest text-muted-foreground">
Free resource
</span>
</div>
<DialogTitle className="text-2xl font-extrabold leading-tight">
Get the free guide
</DialogTitle>
<DialogDescription className="text-base text-muted-foreground mt-1">
The exact playbook we used to grow 14 clients from zero to $100K+.
No fluff, just what works.
</DialogDescription>
</DialogHeader>
<form onSubmit={handleSubmit} className="space-y-3">
<Input
type="email"
placeholder="your@email.com"
value={email}
onChange={(e) => setEmail(e.target.value)}
required
className="h-11"
/>
<Button type="submit" className="w-full h-11 gap-2 text-base font-semibold">
Send me the guide
<ArrowRight className="h-4 w-4" />
</Button>
</form>
<p className="mt-4 text-center text-xs text-muted-foreground">
Join{" "}
<span className="font-semibold text-foreground">1,200+ marketers</span>.
No spam. Unsubscribe anytime.
</p>
</>
)}
</div>
</DialogContent>
</Dialog>
</div>
);
} Claude Code Instructions
CLI Install
npx innovations add newsletter-popupWhere to use it
Add this to your root layout so it appears site-wide after 2 seconds.
In Next.js root layout (app/layout.tsx):
import NewsletterPopup from '@/components/innovations/modals/newsletter-popup';
// Render inside <body>
In Astro root layout:
<NewsletterPopup client:load />
The auto-show timer is inside useEffect so it only runs in the browser (SSR-safe).
Wire the form's onSubmit to your email service (Mailchimp, ConvertKit, Resend, etc.).
Consider adding localStorage logic to suppress the popup for users who already subscribed.