Docs
Newsletter Footer

Newsletter Footer

A stunning footer with integrated newsletter signup. Features dark theme with gradient background and modern glassmorphism design.

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)}
            />
        </>
    );
}
<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

PropTypeDefaultDescription
companyNamestringRequiredCompany name
descriptionstringundefinedCompany description
newsletterTitlestring"Subscribe to our newsletter"Newsletter section title
newsletterDescriptionstring"Get the latest updates..."Newsletter description
columnsFooterColumn[]RequiredLink columns
socialLinksSocialLink[]undefinedSocial media links
bottomLinksFooterLink[]undefinedBottom row links
copyrightTextstringAuto-generatedCustom copyright text
onNewsletterSubmit(email: string) => voidundefinedNewsletter 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