footers /
Newsletter Footer
Three link columns plus a wide newsletter signup form. Submit logs to console — wire to a real endpoint in use.
Preview
Source
tsx
"use client";
import { useState, FormEvent } from "react";
import { Button } from "@/components/ui/button";
import { Sparkles } from "lucide-react";
const columns = [
{ heading: "Product", links: ["Features", "Pricing", "Changelog", "Roadmap"] },
{ heading: "Company", links: ["About", "Blog", "Careers", "Press"] },
{ heading: "Resources", links: ["Docs", "Guides", "API reference", "Status"] },
];
export default function FooterNewsletter() {
const [email, setEmail] = useState("");
const [submitted, setSubmitted] = useState(false);
const onSubmit = (e: FormEvent<HTMLFormElement>) => {
e.preventDefault();
// eslint-disable-next-line no-console
console.log("Newsletter signup:", email);
setSubmitted(true);
setEmail("");
};
return (
<footer className="w-full border-t border-border bg-background">
<div className="container mx-auto px-6 py-12">
<div className="grid grid-cols-2 md:grid-cols-5 gap-8">
{columns.map((col) => (
<div key={col.heading}>
<h3 className="text-sm font-semibold text-foreground mb-4">{col.heading}</h3>
<ul className="space-y-2">
{col.links.map((l) => (
<li key={l}>
<a href="#" className="text-sm text-muted-foreground hover:text-foreground transition-colors">
{l}
</a>
</li>
))}
</ul>
</div>
))}
<div className="col-span-2">
<h3 className="text-sm font-semibold text-foreground mb-4">
Get the newsletter
</h3>
<p className="text-sm text-muted-foreground mb-3">
Monthly product updates and essays. No spam.
</p>
<form onSubmit={onSubmit} className="flex gap-2">
<input
type="email"
required
value={email}
onChange={(e) => setEmail(e.target.value)}
placeholder="you@company.com"
className="flex-1 min-w-0 rounded-md border border-border bg-background px-3 py-2 text-sm text-foreground placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-primary/50"
/>
<Button type="submit" size="sm">Subscribe</Button>
</form>
{submitted && (
<p className="text-xs text-primary mt-2">Thanks — check your inbox.</p>
)}
</div>
</div>
<div className="mt-10 pt-6 border-t border-border flex items-center justify-between gap-4">
<a href="#" className="flex items-center gap-2 text-foreground font-bold">
<Sparkles className="w-5 h-5 text-primary" />
<span>Acme</span>
</a>
<p className="text-xs text-muted-foreground">
© {new Date().getFullYear()} Acme, Inc.
</p>
</div>
</div>
</footer>
);
} Claude Code Instructions
CLI Install
npx innovations add newsletterWhere to use it
Use when the newsletter is a primary growth channel.
In Astro (src/layouts/Layout.astro):
import FooterNewsletter from '../components/innovations/footers/newsletter';
IMPORTANT: the onSubmit handler currently console.logs the email. Replace with a real API call (e.g., Resend, Beehiiv, ConvertKit, or a /api/subscribe endpoint) before launch. The submit button is a shadcn Button — add a loading state if your endpoint is slow.
Customize: edit the columns array at the top. Keep to 3 columns so the newsletter column has room to breathe (col-span-2 on md+).