Footer
Full site footer with link columns, newsletter and socials.
components/ui/footer-section.tsx
"use client";
import { motion } from "motion/react";
import { ArrowRight, Github, Hexagon, Linkedin, Twitter } from "lucide-react";
const columns = {
Product: ["Features", "Pricing", "Integrations", "Changelog"],
Company: ["About", "Careers", "Blog", "Press"],
Resources: ["Docs", "Guides", "API", "Status"],
};
export function FooterSection({ brand = "Acme" }: { brand?: string }) {
return (
<footer className="w-full rounded-3xl border border-zinc-200 bg-white px-8 pb-8 pt-12">
<div className="grid gap-10 lg:grid-cols-[1.5fr_1fr_1fr_1fr]">
<div>
<span className="flex items-center gap-2 font-semibold text-zinc-900">
<Hexagon className="size-5 fill-zinc-900" /> {brand}
</span>
<p className="mt-3 max-w-xs text-sm text-zinc-500">Tools for teams that ship. Get product updates once a month.</p>
<form onSubmit={(e) => e.preventDefault()} className="mt-5 flex max-w-xs gap-2">
<input type="email" placeholder="[email protected]" aria-label="Email" className="min-w-0 flex-1 rounded-lg border border-zinc-200 px-3 py-2 text-sm outline-none focus:border-zinc-400" />
<button aria-label="Subscribe" className="grid size-9 shrink-0 place-items-center rounded-lg bg-zinc-900 text-white hover:bg-zinc-800">
<ArrowRight className="size-4" />
</button>
</form>
</div>
{Object.entries(columns).map(([title, links]) => (
<div key={title}>
<p className="text-sm font-semibold text-zinc-900">{title}</p>
<ul className="mt-4 space-y-2.5">
{links.map((l) => (
<li key={l}>
<a href="#" className="group inline-flex items-center gap-1 text-sm text-zinc-500 transition-colors hover:text-zinc-900">
{l}
<ArrowRight className="size-3 -translate-x-1 opacity-0 transition-all group-hover:translate-x-0 group-hover:opacity-100" />
</a>
</li>
))}
</ul>
</div>
))}
</div>
<div className="mt-12 flex flex-wrap items-center justify-between gap-4 border-t border-zinc-100 pt-6 text-sm text-zinc-500">
<span>© {new Date().getFullYear()} {brand}, Inc.</span>
<div className="flex gap-2">
{[Twitter, Github, Linkedin].map((Icon, i) => (
<motion.a key={i} href="#" aria-label="Social link" whileHover={{ y: -3 }} className="grid size-9 place-items-center rounded-full border border-zinc-200 text-zinc-600 hover:text-zinc-900">
<Icon className="size-4" />
</motion.a>
))}
</div>
</div>
</footer>
);
}
export default function Demo() {
return (
<div className="w-full max-w-5xl">
<FooterSection />
</div>
);
}
Installation
Install the dependencies
$ npm i motion lucide-reactCopy the source into
components/ui/footer-section.tsxcomponents/ui/footer-section.tsx"use client"; import { motion } from "motion/react"; import { ArrowRight, Github, Hexagon, Linkedin, Twitter } from "lucide-react"; const columns = { Product: ["Features", "Pricing", "Integrations", "Changelog"], Company: ["About", "Careers", "Blog", "Press"], Resources: ["Docs", "Guides", "API", "Status"], }; export function FooterSection({ brand = "Acme" }: { brand?: string }) { return ( <footer className="w-full rounded-3xl border border-zinc-200 bg-white px-8 pb-8 pt-12"> <div className="grid gap-10 lg:grid-cols-[1.5fr_1fr_1fr_1fr]"> <div> <span className="flex items-center gap-2 font-semibold text-zinc-900"> <Hexagon className="size-5 fill-zinc-900" /> {brand} </span> <p className="mt-3 max-w-xs text-sm text-zinc-500">Tools for teams that ship. Get product updates once a month.</p> <form onSubmit={(e) => e.preventDefault()} className="mt-5 flex max-w-xs gap-2"> <input type="email" placeholder="[email protected]" aria-label="Email" className="min-w-0 flex-1 rounded-lg border border-zinc-200 px-3 py-2 text-sm outline-none focus:border-zinc-400" /> <button aria-label="Subscribe" className="grid size-9 shrink-0 place-items-center rounded-lg bg-zinc-900 text-white hover:bg-zinc-800"> <ArrowRight className="size-4" /> </button> </form> </div> {Object.entries(columns).map(([title, links]) => ( <div key={title}> <p className="text-sm font-semibold text-zinc-900">{title}</p> <ul className="mt-4 space-y-2.5"> {links.map((l) => ( <li key={l}> <a href="#" className="group inline-flex items-center gap-1 text-sm text-zinc-500 transition-colors hover:text-zinc-900"> {l} <ArrowRight className="size-3 -translate-x-1 opacity-0 transition-all group-hover:translate-x-0 group-hover:opacity-100" /> </a> </li> ))} </ul> </div> ))} </div> <div className="mt-12 flex flex-wrap items-center justify-between gap-4 border-t border-zinc-100 pt-6 text-sm text-zinc-500"> <span>© {new Date().getFullYear()} {brand}, Inc.</span> <div className="flex gap-2"> {[Twitter, Github, Linkedin].map((Icon, i) => ( <motion.a key={i} href="#" aria-label="Social link" whileHover={{ y: -3 }} className="grid size-9 place-items-center rounded-full border border-zinc-200 text-zinc-600 hover:text-zinc-900"> <Icon className="size-4" /> </motion.a> ))} </div> </div> </footer> ); } export default function Demo() { return ( <div className="w-full max-w-5xl"> <FooterSection /> </div> ); }Use it
The file exports the component and a
Demoshowing typical usage. Import whichever you need.