Newsletter
Stay in the loop
Get the latest updates, articles, and resources delivered to your inbox weekly.
- Weekly curated content
- Exclusive tips and tricks
- Early access to new features
Newsletter
Join our community
Subscribe to get insider updates and special offers.
- Product updates and news
- Special discounts
- Community highlights
Newsletter
Get weekly insights
Join 10,000+ subscribers getting actionable tips every week.
- Expert insights
- Case studies
- Industry trends
Newsletter
Never miss an update
Be the first to know about new features and updates.
- Product announcements
- Feature tutorials
- Best practices
Features
- ✅ Vibrant colors - Blue, Purple, Green, Orange variants
- ✅ Split layout - Content and form side-by-side
- ✅ Benefits list - Checkmarks for key value props
- ✅ Email validation - Built-in form validation
- ✅ Decorative elements - Subtle glows and grid pattern
- ✅ Newsletter badge - Mail icon with label
- ✅ Privacy note - "No spam" reassurance
- ✅ Responsive - Stacks beautifully on mobile
- ✅ TypeScript support - Full type safety
Installation
Copy and paste the following code into your project.
"use client";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Check, Loader2, Mail } from "lucide-react";
import { useState } from "react";
interface NewsletterSignupProps {
headline: string;
description?: string;
benefits?: string[];
variant?: "blue" | "purple" | "green" | "orange";
placeholder?: string;
buttonText?: string;
onSubmit?: (email: string) => void;
}
const variants = {
blue: {
section: "bg-blue-600",
headline: "text-white",
description: "text-blue-100",
benefits: "text-blue-50",
input:
"bg-white/10 border-white/20 text-white placeholder:text-white/60 focus:bg-white/20",
button: "bg-white text-blue-600 hover:bg-blue-50",
checkmark: "bg-white/20 text-white",
successBg: "bg-white/10",
},
purple: {
section: "bg-purple-600",
headline: "text-white",
description: "text-purple-100",
benefits: "text-purple-50",
input:
"bg-white/10 border-white/20 text-white placeholder:text-white/60 focus:bg-white/20",
button: "bg-white text-purple-600 hover:bg-purple-50",
checkmark: "bg-white/20 text-white",
successBg: "bg-white/10",
},
green: {
section: "bg-green-600",
headline: "text-white",
description: "text-green-100",
benefits: "text-green-50",
input:
"bg-white/10 border-white/20 text-white placeholder:text-white/60 focus:bg-white/20",
button: "bg-white text-green-600 hover:bg-green-50",
checkmark: "bg-white/20 text-white",
successBg: "bg-white/10",
},
orange: {
section: "bg-orange-600",
headline: "text-white",
description: "text-orange-100",
benefits: "text-orange-50",
input:
"bg-white/10 border-white/20 text-white placeholder:text-white/60 focus:bg-white/20",
button: "bg-white text-orange-600 hover:bg-orange-50",
checkmark: "bg-white/20 text-white",
successBg: "bg-white/10",
},
};
export function NewsletterSignup({
headline,
description,
benefits,
variant = "blue",
placeholder = "Enter your email",
buttonText = "Subscribe",
onSubmit,
}: NewsletterSignupProps) {
const [email, setEmail] = useState("");
const [isLoading, setIsLoading] = useState(false);
const [isSuccess, setIsSuccess] = useState(false);
const styles = variants[variant];
const successMessage = "Check your inbox to confirm your subscription.";
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
if (email && onSubmit) {
setIsLoading(true);
try {
await onSubmit(email);
setIsSuccess(true);
setEmail("");
} catch (error) {
console.error("Subscription error:", error);
} finally {
setIsLoading(false);
}
}
};
return (
<section
className={`relative overflow-hidden py-16 sm:py-20 ${styles.section}`}
>
<div className="absolute inset-0 bg-[linear-gradient(to_right,#ffffff08_1px,transparent_1px),linear-gradient(to_bottom,#ffffff08_1px,transparent_1px)] bg-[size:24px_24px]" />
<div className="absolute right-0 top-0 h-64 w-64 rounded-full bg-white/5 blur-3xl" />
<div className="absolute bottom-0 left-0 h-64 w-64 rounded-full bg-white/5 blur-3xl" />
<div className="container relative mx-auto px-6">
<div className="mx-auto max-w-4xl">
<div className="grid gap-8 lg:grid-cols-2 lg:gap-12">
<div>
<div className="mb-2 inline-flex items-center gap-2 rounded-full bg-white/10 px-3 py-1">
<Mail className="h-4 w-4 text-white" />
<span className="text-sm font-medium text-white">
Newsletter
</span>
</div>
<h2
className={`mb-4 text-3xl font-bold tracking-tight sm:text-4xl ${styles.headline}`}
>
{headline}
</h2>
{description && (
<p className={`mb-6 text-lg ${styles.description}`}>
{description}
</p>
)}
{benefits && benefits.length > 0 && (
<ul className="space-y-3">
{benefits.map((benefit, index) => (
<li key={index} className="flex items-start gap-3">
<div
className={`mt-0.5 flex h-5 w-5 flex-shrink-0 items-center justify-center rounded-full ${styles.checkmark}`}
>
<Check className="h-3.5 w-3.5 stroke-[3]" />
</div>
<span className={`text-base ${styles.benefits}`}>
{benefit}
</span>
</li>
))}
</ul>
)}
</div>
<div className="flex items-center">
<div className="w-full">
{isSuccess ? (
<div
className={`animate-in fade-in slide-in-from-bottom-4 rounded-2xl p-8 text-center duration-500 ${styles.successBg}`}
>
<div className="mx-auto mb-4 flex h-16 w-16 items-center justify-center rounded-full bg-white/20">
<Check className="h-8 w-8 text-white" />
</div>
<h3 className="mb-2 text-xl font-bold text-white">
You're all set!
</h3>
<p className="text-white/80">{successMessage}</p>
</div>
) : (
<form onSubmit={handleSubmit} className="space-y-4">
<div className="space-y-3">
<Input
type="email"
placeholder={placeholder}
value={email}
onChange={(e) => setEmail(e.target.value)}
required
disabled={isLoading}
className={`h-12 text-base transition-all duration-200 ${styles.input}`}
/>
<Button
type="submit"
size="lg"
disabled={isLoading}
className={`w-full font-semibold transition-all duration-200 ${styles.button}`}
>
{isLoading ? (
<>
<Loader2 className="mr-2 h-4 w-4 animate-spin" />
Subscribing...
</>
) : (
buttonText
)}
</Button>
</div>
<p className="text-center text-sm text-white/60">
No spam. Unsubscribe anytime.
</p>
</form>
)}
</div>
</div>
</div>
</div>
</div>
</section>
);
}Update the import paths to match your project setup.
import { Input } from "@/components/ui/input";Usage
Basic Usage
import NewsletterSignup from "@/components/ui/newsletter-signup";
export default function Page() {
return (
<NewsletterSignup
headline="Stay in the loop"
description="Get weekly updates delivered to your inbox."
onSubmit={(email) => console.log("Subscribed:", email)}
/>
);
}With Benefits List
<NewsletterSignup
headline="Join our community"
description="Subscribe to get insider updates."
benefits={[
"Weekly curated content",
"Exclusive tips and tricks",
"Early access to new features",
]}
onSubmit={(email) => console.log(email)}
/>Color Variants
{/* Blue - Professional and trustworthy */}
<NewsletterSignup
variant="blue"
headline="Stay in the loop"
onSubmit={(email) => console.log(email)}
/>
{/* Purple - Creative and modern */}
<NewsletterSignup
variant="purple"
headline="Join our community"
onSubmit={(email) => console.log(email)}
/>
{/* Green - Growth and success */}
<NewsletterSignup
variant="green"
headline="Get weekly insights"
onSubmit={(email) => console.log(email)}
/>
{/* Orange - Energetic and bold */}
<NewsletterSignup
variant="orange"
headline="Never miss an update"
onSubmit={(email) => console.log(email)}
/>Custom Text
<NewsletterSignup
headline="Join 10,000+ subscribers"
description="Get actionable tips every week."
placeholder="you@example.com"
buttonText="Sign Me Up"
onSubmit={(email) => console.log(email)}
/>With API Integration
"use client";
import { useState } from "react";
import NewsletterSignup from "@/components/ui/newsletter-signup";
export default function Page() {
const [isLoading, setIsLoading] = useState(false);
const handleSubmit = async (email: string) => {
setIsLoading(true);
try {
const response = await fetch("/api/newsletter", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ email }),
});
if (response.ok) {
alert("Successfully subscribed!");
}
} catch (error) {
console.error("Subscription failed:", error);
} finally {
setIsLoading(false);
}
};
return (
<NewsletterSignup
headline="Stay updated"
onSubmit={handleSubmit}
/>
);
}Props
| Prop | Type | Default | Description |
|---|---|---|---|
headline | string | Required | Main headline text |
description | string | undefined | Optional description |
benefits | string[] | undefined | List of benefits with checkmarks |
variant | "blue" | "purple" | "green" | "orange" | "blue" | Color theme |
placeholder | string | "Enter your email" | Input placeholder text |
buttonText | string | "Subscribe" | Submit button text |
onSubmit | (email: string) => void | undefined | Form submit handler |
TypeScript Interface
interface NewsletterSignupProps {
headline: string;
description?: string;
benefits?: string[];
variant?: "blue" | "purple" | "green" | "orange";
placeholder?: string;
buttonText?: string;
onSubmit?: (email: string) => void;
}Color Psychology
Blue (variant="blue")
- Trust, professionalism, reliability
- Best for: B2B, finance, tech companies
Purple (variant="purple")
- Creativity, luxury, innovation
- Best for: Creative agencies, premium products
Green (variant="green")
- Growth, health, success
- Best for: Sustainability, wellness, finance
Orange (variant="orange")
- Energy, enthusiasm, action
- Best for: E-commerce, events, startups
Customization
Add Loading State
const [isLoading, setIsLoading] = useState(false);
<Button
type="submit"
disabled={isLoading}
className={styles.button}
>
{isLoading ? "Subscribing..." : buttonText}
</Button>Success Message
const [isSubscribed, setIsSubscribed] = useState(false);
{isSubscribed ? (
<div className="text-center text-white">
<Check className="mx-auto h-12 w-12 mb-2" />
<p>Thanks for subscribing!</p>
</div>
) : (
<form onSubmit={handleSubmit}>
{/* form content */}
</form>
)}Custom Colors
Add your own variant to the variants object:
const variants = {
// ... existing variants
custom: {
section: "bg-pink-600",
headline: "text-white",
description: "text-pink-100",
benefits: "text-pink-50",
input: "bg-white/10 border-white/20 text-white placeholder:text-white/60",
button: "bg-white text-pink-600 hover:bg-pink-50",
checkmark: "bg-white/20 text-white",
},
};Use Cases
Perfect for:
- Blog subscriptions
- Product updates
- Weekly newsletters
- Marketing campaigns
- Lead generation
- Community building
- Event notifications
- Course enrollments