Features
- ✅ Integrated newsletter - Prominent email signup section
- ✅ Dark gradient theme - Gray-900 to black gradient
- ✅ Glassmorphism card - Frosted glass effect for newsletter
- ✅ Success animation - Inline confirmation message
- ✅ Loading states - Visual feedback during submission
- ✅ Social media icons - Circular icon buttons
- ✅ Multi-column links - Organized link sections
- ✅ Bottom links - Legal and additional links
- ✅ Grid pattern - Subtle background decoration
- ✅ 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,
Facebook,
Github,
Instagram,
Linkedin,
Mail,
Twitter,
} from "lucide-react";
import Link from "next/link";
import { useState } from "react";
interface FooterLink {
label: string;
href: string;
}
interface FooterColumn {
title: string;
links: FooterLink[];
}
interface SocialLink {
icon: "twitter" | "facebook" | "instagram" | "linkedin" | "github";
href: string;
label: string;
}
interface FooterNewsletterProps {
companyName: string;
description?: string;
newsletterTitle?: string;
newsletterDescription?: string;
columns: FooterColumn[];
socialLinks?: SocialLink[];
bottomLinks?: FooterLink[];
copyrightText?: string;
onNewsletterSubmit?: (email: string) => Promise<void> | void;
}
const socialIcons = {
twitter: Twitter,
facebook: Facebook,
instagram: Instagram,
linkedin: Linkedin,
github: Github,
};
export function FooterNewsletter({
companyName,
description,
newsletterTitle = "Subscribe to our newsletter",
newsletterDescription = "Get the latest updates delivered to your inbox.",
columns,
socialLinks,
bottomLinks,
copyrightText,
onNewsletterSubmit,
}: FooterNewsletterProps) {
const [email, setEmail] = useState("");
const [isLoading, setIsLoading] = useState(false);
const [isSuccess, setIsSuccess] = useState(false);
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
if (email && onNewsletterSubmit) {
setIsLoading(true);
try {
await onNewsletterSubmit(email);
setIsSuccess(true);
setEmail("");
setTimeout(() => setIsSuccess(false), 3000);
} catch (error) {
console.error("Newsletter signup error:", error);
} finally {
setIsLoading(false);
}
}
};
return (
<footer className="relative overflow-hidden bg-gradient-to-b from-gray-900 to-black text-white rounded">
<div className="absolute inset-0 bg-[linear-gradient(to_right,#ffffff05_1px,transparent_1px),linear-gradient(to_bottom,#ffffff05_1px,transparent_1px)] bg-[size:32px_32px]" />
<div className="container relative mx-auto px-6 py-16">
<div className="mb-16 rounded-2xl border border-white/10 bg-white/5 p-8 backdrop-blur-sm lg:p-12">
<div className="grid gap-8 lg:grid-cols-2 lg:gap-12">
<div>
<div className="mb-4 inline-flex items-center gap-2 rounded-full bg-blue-500/10 px-3 py-1">
<Mail className="h-4 w-4 text-blue-400" />
<span className="text-sm font-semibold text-blue-400">
Newsletter
</span>
</div>
<h3 className="mb-3 text-3xl font-bold">{newsletterTitle}</h3>
<p className="text-gray-400">{newsletterDescription}</p>
</div>
<div className="flex items-center">
{isSuccess ? (
<div className="flex w-full items-center gap-3 rounded-xl bg-green-500/10 p-4">
<div className="flex h-10 w-10 flex-shrink-0 items-center justify-center rounded-full bg-green-500">
<Check className="h-5 w-5 text-white" strokeWidth={3} />
</div>
<div>
<p className="font-semibold text-green-400">
Successfully subscribed!
</p>
<p className="text-sm text-gray-400">
Check your inbox for confirmation.
</p>
</div>
</div>
) : (
<form onSubmit={handleSubmit} className="w-full">
<div className="flex flex-col gap-3 sm:flex-row">
<Input
type="email"
placeholder="Enter your email"
value={email}
onChange={(e) => setEmail(e.target.value)}
required
disabled={isLoading}
className="h-12 flex-1 rounded-lg border-white/10 bg-white/5 text-white placeholder:text-gray-500 focus:border-blue-500 focus:ring-blue-500"
/>
<Button
type="submit"
disabled={isLoading}
className="h-12 rounded-lg bg-blue-600 px-6 font-semibold text-white hover:bg-blue-700"
>
{isLoading ? "Subscribing..." : "Subscribe"}
</Button>
</div>
</form>
)}
</div>
</div>
</div>
<div className="mb-12 grid gap-8 sm:grid-cols-2 lg:grid-cols-12 lg:gap-12">
<div className="lg:col-span-4">
<Link href="/" className="inline-block">
<span className="text-2xl font-bold">{companyName}</span>
</Link>
{description && (
<p className="mt-4 text-sm text-gray-400">{description}</p>
)}
{socialLinks && socialLinks.length > 0 && (
<div className="mt-6 flex items-center gap-4">
{socialLinks.map((social, index) => {
const Icon = socialIcons[social.icon];
return (
<Link
key={index}
href={social.href}
aria-label={social.label}
className="flex h-10 w-10 items-center justify-center rounded-full bg-white/5 text-gray-400 transition-all hover:bg-white/10 hover:text-white"
>
<Icon className="h-5 w-5" />
</Link>
);
})}
</div>
)}
</div>
<div className="grid grid-cols-2 gap-8 sm:grid-cols-3 lg:col-span-8 lg:grid-cols-3">
{columns.map((column, index) => (
<div key={index}>
<h4 className="mb-4 text-sm font-semibold uppercase tracking-wider text-gray-400">
{column.title}
</h4>
<ul className="space-y-3">
{column.links.map((link, linkIndex) => (
<li key={linkIndex}>
<Link
href={link.href}
className="text-sm text-gray-400 transition-colors hover:text-white"
>
{link.label}
</Link>
</li>
))}
</ul>
</div>
))}
</div>
</div>
<div className="border-t border-white/10 pt-8">
<div className="flex flex-col items-center justify-between gap-4 sm:flex-row">
<p className="text-sm text-gray-400">
{copyrightText ||
`© ${new Date().getFullYear()} ${companyName}. All rights reserved.`}
</p>
{bottomLinks && bottomLinks.length > 0 && (
<div className="flex flex-wrap items-center gap-6">
{bottomLinks.map((link, index) => (
<Link
key={index}
href={link.href}
className="text-sm text-gray-400 transition-colors hover:text-white"
>
{link.label}
</Link>
))}
</div>
)}
</div>
</div>
</div>
</footer>
);
}Update the import paths to match your project setup.
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";Usage
Basic Usage
import FooterNewsletter from "@/components/ui/footer-newsletter";
export default function Layout({ children }) {
return (
<>
{children}
<FooterNewsletter
companyName="Acme Inc"
description="Building amazing products."
columns={[
{
title: "Product",
links: [
{ label: "Features", href: "/features" },
{ label: "Pricing", href: "/pricing" },
],
},
]}
onNewsletterSubmit={(email) => console.log(email)}
/>
</>
);
}With Social Links
<FooterNewsletter
companyName="Acme Inc"
columns={[...]}
socialLinks={[
{ icon: "twitter", href: "https://twitter.com", label: "Twitter" },
{ icon: "github", href: "https://github.com", label: "GitHub" },
{ icon: "linkedin", href: "https://linkedin.com", label: "LinkedIn" },
]}
onNewsletterSubmit={(email) => console.log(email)}
/>Custom Newsletter Text
<FooterNewsletter
companyName="Acme Inc"
newsletterTitle="Join our community"
newsletterDescription="Get exclusive tips and updates every week."
columns={[...]}
onNewsletterSubmit={(email) => console.log(email)}
/>With API Integration
"use client";
import FooterNewsletter from "@/components/ui/footer-newsletter";
export default function Layout({ children }) {
const handleNewsletterSubmit = async (email: string) => {
try {
const response = await fetch("/api/newsletter", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ email }),
});
if (response.ok) {
// Success handled by component
}
} catch (error) {
console.error("Newsletter signup failed:", error);
}
};
return (
<>
{children}
<FooterNewsletter
companyName="Acme Inc"
columns={[...]}
onNewsletterSubmit={handleNewsletterSubmit}
/>
</>
);
}Props
| Prop | Type | Default | Description |
|---|---|---|---|
companyName | string | Required | Company name |
description | string | undefined | Company description |
newsletterTitle | string | "Subscribe to our newsletter" | Newsletter section title |
newsletterDescription | string | "Get the latest updates..." | Newsletter description |
columns | FooterColumn[] | Required | Link columns |
socialLinks | SocialLink[] | undefined | Social media links |
bottomLinks | FooterLink[] | undefined | Bottom row links |
copyrightText | string | Auto-generated | Custom copyright text |
onNewsletterSubmit | (email: string) => void | undefined | Newsletter submit handler |
TypeScript Interface
interface FooterNewsletterProps {
companyName: string;
description?: string;
newsletterTitle?: string;
newsletterDescription?: string;
columns: FooterColumn[];
socialLinks?: SocialLink[];
bottomLinks?: FooterLink[];
copyrightText?: string;
onNewsletterSubmit?: (email: string) => Promise<void> | void;
}
interface FooterLink {
label: string;
href: string;
}
interface FooterColumn {
title: string;
links: FooterLink[];
}
interface SocialLink {
icon: "twitter" | "facebook" | "instagram" | "linkedin" | "github";
href: string;
label: string;
}Use Cases
Perfect for:
- Marketing websites
- SaaS products
- Blog platforms
- E-commerce sites
- Landing pages
- Portfolio sites
- Community platforms
- Content sites