Footer Design in React: 5 Patterns From Minimal to Full-Featured
Five practical React footer patterns — from a dead-simple single-row layout to a full multi-column sitemap — with Tailwind code you can drop in today.
Why Footer Design Still Matters in 2026
The footer is the last thing a user sees — and it's also one of the most under-engineered parts of most React apps. Designers treat it as an afterthought. Developers copy-paste something from a previous project and call it done. Meanwhile, it's sitting there failing at basic jobs: helping lost users navigate, passing link equity to deep pages, and reinforcing brand credibility right before someone decides whether to convert.
Honestly, a bad footer is a silent conversion killer. If someone scrolls to the bottom of your page and hits a copyright line with zero useful links, you've just told them your product isn't serious.
What follows are five patterns you'll actually reach for — ordered from the simplest possible implementation to a production-grade, multi-column footer that handles real navigation needs. Each one has trade-offs worth understanding before you pick.
Pattern 1 — The Minimal One-Liner
Start here when you're building an app that doesn't need marketing-style navigation. A SaaS dashboard, an internal tool, a landing page that's basically a single CTA. The rule is: one row, flex layout, copyright left, two or three links right.
export function MinimalFooter() {
return (
<footer className="border-t border-zinc-200 px-6 py-4 flex items-center justify-between text-sm text-zinc-500">
<span>© 2026 YourApp, Inc.</span>
<nav className="flex gap-6">
<a href="/privacy" className="hover:text-zinc-900 transition-colors">Privacy</a>
<a href="/terms" className="hover:text-zinc-900 transition-colors">Terms</a>
<a href="/contact" className="hover:text-zinc-900 transition-colors">Contact</a>
</nav>
</footer>
);
}The border-t gives it enough separation without adding visual weight. Keep the font size at 14px (Tailwind's text-sm) — anything larger starts competing with your actual content. That said, don't strip the hover state; even a minimal footer needs to signal interactivity.
Worth noting: this pattern breaks at around 480px if you have more than three links. Wrap it in a flex-col gap-2 sm:flex-row and you're fine on mobile without any extra work.
Pattern 2 — The Brand Strip
One step up from the one-liner. You want your logo present, a short tagline, and a handful of social icons. This works well for landing pages and marketing sites where you're not shipping a full sitemap but still want the footer to feel intentional.
The key layout decision here is centering everything — logo, tagline, socials — which makes it feel designed rather than assembled. Use 48px min-height on the icon tap targets if you're worried about mobile usability. Center-aligned footers can feel weak on wide viewports, so constrain the max-width to around 640px and let the background fill the rest.
In practice, this is the pattern most design-forward SaaS products default to once they realize the minimal one-liner feels too spare but they don't have the content for a full sitemap yet. It's an honest middle ground.
If you're working with styled components or a design system, this is also the footer pattern that benefits most from a consistent color token — something like bg-zinc-950 with text-zinc-400 links. Browse the components on Empire UI for a set of pre-built social icon variants that slot right into this layout.
Pattern 3 — The Two-Column Newsletter Footer
This one earns its keep on content sites, blogs, and product marketing pages where you want to capture emails at the very bottom of the funnel. Someone read your entire page, scrolled to the end — that's a warm lead. Don't waste it on a copyright line.
The layout is left column: brand logo plus a one-sentence description. Right column: email input plus a submit button. At mobile breakpoints it stacks. Tailwind makes this trivial with grid grid-cols-1 md:grid-cols-2 gap-8.
export function NewsletterFooter() {
return (
<footer className="bg-zinc-950 text-white px-8 py-12">
<div className="max-w-5xl mx-auto grid grid-cols-1 md:grid-cols-2 gap-8 items-center">
<div>
<p className="text-lg font-semibold">YourApp</p>
<p className="mt-2 text-sm text-zinc-400">Ship better UIs, faster.</p>
</div>
<form className="flex flex-col sm:flex-row gap-3">
<input
type="email"
placeholder="you@example.com"
className="flex-1 rounded-md px-4 py-2 bg-zinc-800 border border-zinc-700 text-sm text-white placeholder:text-zinc-500 focus:outline-none focus:ring-2 focus:ring-indigo-500"
/>
<button
type="submit"
className="rounded-md bg-indigo-600 px-5 py-2 text-sm font-medium hover:bg-indigo-500 transition-colors"
>
Subscribe
</button>
</form>
</div>
<div className="max-w-5xl mx-auto mt-8 pt-6 border-t border-zinc-800 text-xs text-zinc-500">
© 2026 YourApp. All rights reserved.
</div>
</footer>
);
}One more thing — wire the form to a real endpoint before shipping. A footer email input that does nothing on submit is worse than no email input at all; you're actively training users that your UI lies.
Pattern 4 — The Three-Column Sitemap Footer
This is the workhorse. Product, Resources, Company — three columns of links, logo top-left, copyright bottom-left, social icons bottom-right. You've seen it on basically every B2B SaaS homepage since about 2019.
The reason it's everywhere isn't because designers lack imagination — it's because it works. It gives users a scannable map of everything your product does without requiring them to hunt through a nav menu. And it does solid SEO work by linking internal pages with descriptive anchor text.
Structure it with CSS Grid. Three columns at lg:, two at md:, one on mobile. Each column is a <nav> with an aria-label matching the column heading — this matters for screen readers and it's a 10-second addition. Link headings should be uppercase, 12px, tracked — they're labels, not navigation targets.
Look, if your product has fewer than six total pages, this pattern will feel empty and over-engineered. Fill empty columns with something real — a featured blog post, a changelog entry, a product status badge — or drop back to two columns.
Pattern 5 — The Full-Featured Mega Footer
Four or five columns, a distinct background section at the top for a CTA or newsletter, legal links in a separate sub-footer row. This is the pattern you build once when the site has reached a level of content density that demands it — documentation sites, large SaaS platforms, e-commerce.
The structural challenge here is maintainability. Hardcoding sixty links into a JSX component means the next time your nav changes, you're hunting through markup. Instead, define your link tree as data and map over it.
const FOOTER_LINKS = [
{
label: "Product",
links: [
{ text: "Components", href: "/" },
{ text: "Templates", href: "/templates" },
{ text: "Tools", href: "/tools" },
],
},
{
label: "Design",
links: [
{ text: "Glassmorphism", href: "/glassmorphism" },
{ text: "Neumorphism", href: "/neumorphism" },
{ text: "Neobrutalism", href: "/neobrutalism" },
],
},
{
label: "Company",
links: [
{ text: "Blog", href: "/blog" },
{ text: "MCP", href: "/mcp" },
{ text: "Contact", href: "/contact" },
],
},
];
export function MegaFooter() {
return (
<footer className="bg-zinc-950 text-zinc-300">
<div className="max-w-7xl mx-auto px-8 py-16 grid grid-cols-2 md:grid-cols-4 gap-10">
<div className="col-span-2 md:col-span-1">
<p className="text-white font-bold text-lg">Empire UI</p>
<p className="mt-3 text-sm text-zinc-500 leading-relaxed">
React components for ambitious UIs.
</p>
</div>
{FOOTER_LINKS.map((col) => (
<nav key={col.label} aria-label={col.label}>
<p className="text-xs font-semibold uppercase tracking-widest text-zinc-500 mb-4">
{col.label}
</p>
<ul className="space-y-3">
{col.links.map((link) => (
<li key={link.href}>
<a
href={link.href}
className="text-sm hover:text-white transition-colors"
>
{link.text}
</a>
</li>
))}
</ul>
</nav>
))}
</div>
<div className="border-t border-zinc-800">
<div className="max-w-7xl mx-auto px-8 py-6 flex flex-col sm:flex-row justify-between items-center gap-4 text-xs text-zinc-600">
<span>© 2026 Empire UI. All rights reserved.</span>
<div className="flex gap-6">
<a href="/privacy" className="hover:text-zinc-400">Privacy</a>
<a href="/terms" className="hover:text-zinc-400">Terms</a>
</div>
</div>
</div>
</footer>
);
}The data-driven approach means adding a new section is a three-line object. Removing one doesn't involve touching any JSX. Quick aside: if you're using Next.js App Router, mark this component as a Server Component — it has zero client interactivity needs and shipping it as 'use client' is just dead weight in the bundle.
For visual polish on the mega footer, check out how glassmorphism components handle layered backgrounds — the same technique that gives glass cards their depth works surprisingly well for footer section dividers.
Choosing the Right Pattern and Avoiding Common Mistakes
Map your content volume to the pattern. Fewer than five pages? Minimal or brand strip. Five to fifteen? Two- or three-column. More than that? Mega footer, but only if you'll keep it maintained — a mega footer with dead links is actively harmful.
Three mistakes that cut across all five patterns. First, forgetting role="contentinfo" on your <footer> element — the HTML spec makes it implicit inside <body>, but being explicit costs nothing and helps screen readers skip to it. Second, using onClick navigation instead of real <a> tags — crawlers can't follow JavaScript-only nav. Third, setting display: none on the copyright block at mobile to save space — that's the one line that actually needs to survive every breakpoint.
Worth noting: the gradient generator and box shadow generator are genuinely useful when you're finalizing footer backgrounds and top-edge separation effects. Fifteen minutes there usually beats an hour of trial-and-error in CSS.
Whichever pattern you ship, test it with a keyboard-only user flow. Tab through every footer link. Make sure focus rings are visible — at least a 2px outline with 2px offset is the minimum that passes WCAG 2.1 AA. This is the most commonly skipped step in footer QA, and it's a ten-minute check.
FAQ
Server Component unless you're adding interactivity like a newsletter form submit handler or a theme toggle. Static link trees don't need client JavaScript, so keep them out of the bundle.
Add min-h-screen flex flex-col to your root layout wrapper and mt-auto to the footer element. No position tricks, no JavaScript — just flexbox doing its job.
Use <footer> — it carries an implicit ARIA role of contentinfo when it's a direct child of <body>. Wrap nav columns in <nav aria-label="column name"> for screen reader section landmarks.
Match it to your content. Three columns for mid-size sites is the sweet spot — four or five only if you have enough links to fill them without padding. Empty columns signal a poorly structured site.