Footer 2
Footer 2 is a React and Tailwind CSS layout component from SmoothUI, licensed MIT. Copy it and paste it into your project.
SmoothUI
MIT
layout
What it is
Footer 2 is a React and Tailwind CSS layout component from SmoothUI, licensed MIT. Copy it and paste it into your project.
How to use it
Open it in the catalogue, press copy, and paste it into your project. The prompt you copy carries the code and the rules that tell your agent to reproduce it without changing anything.
Five free copies a month. No card.
Needs
npm i motion
Licence
This component comes from SmoothUI and is redistributed under the MIT licence, which permits commercial use. The copyright notice travels with the code.
Footer 2
"use client";
import SmoothButton from "@/components/smoothui/smooth-button";
import { motion, useReducedMotion } from "motion/react";
const ANIMATION_DURATION = 0.6;
const DELAY_INCREMENT = 0.1;
const HOVER_SCALE = 1.1;
const TAP_SCALE = 0.9;
const DELAY_PRODUCT = DELAY_INCREMENT * 2;
const DELAY_COMPANY = DELAY_INCREMENT * 3;
const DELAY_SUPPORT = DELAY_INCREMENT * 4;
const DELAY_LEGAL = DELAY_INCREMENT * 5;
const DELAY_COPYRIGHT = DELAY_INCREMENT * 6;
interface FooterComplexProps {
companyName?: string;
copyright?: string;
description?: string;
links?: {
product?: Array<{ name: string; url: string }>;
company?: Array<{ name: string; url: string }>;
support?: Array<{ name: string; url: string }>;
legal?: Array<{ name: string; url: string }>;
};
newsletter?: {
title: string;
description: string;
placeholder: string;
buttonText: string;
};
social?: {
twitter?: string;
linkedin?: string;
github?: string;
discord?: string;
youtube?: string;
};
}
export function FooterComplex({
companyName = "Smoothui",
description = "Build beautiful UIs, effortlessly. The modern way to create stunning interfaces with smooth animations.",
newsletter = {
buttonText: "Subscribe",
description: "Get the latest news and updates delivered to your inbox.",
placeholder: "Enter your email",
title: "Stay updated",
},
links = {
company: [
{ name: "About Us", url: "#about" },
{ name: "Blog", url: "#blog" },
{ name: "Careers", url: "#careers" },
{ name: "Press Kit", url: "#press" },
{ name: "Contact", url: "#contact" },
],
legal: [
{ name: "Privacy Policy", url: "#privacy" },
{ name: "Terms of Service", url: "#terms" },
{ name: "Cookie Policy", url: "#cookies" },
{ name: "GDPR", url: "#gdpr" },
],
product: [
{ name: "Features", url: "#features" },
{ name: "Pricing", url: "#pricing" },
{ name: "Documentation", url: "#docs" },
{ name: "API Reference", url: "#api" },
{ name: "Changelog", url: "#changelog" },
],
support: [
{ name: "Help Center", url: "#help" },
{ name: "Community", url: "#community" },
{ name: "Status Page", url: "#status" },
{ name: "Security", url: "#security" },
{ name: "Bug Reports", url: "#bugs" },
],
},
social = {
discord: "https://discord.com",
github: "https://github.com",
linkedin: "https://linkedin.com",
twitter: "https://twitter.com",
youtube: "https://youtube.com",
},
copyright = "© 2024 Smoothui. All rights reserved.",
}: FooterComplexProps) {
const shouldReduceMotion = useReducedMotion();
const socialHover = shouldReduceMotion ? {} : { scale: HOVER_SCALE };
const socialTap = shouldReduceMotion ? {} : { scale: TAP_SCALE };
return (
<footer className="border-border border-t bg-background">
<div className="mx-auto max-w-7xl px-6 py-16">
<motion.div
animate={shouldReduceMotion ? { opacity: 1 } : { opacity: 1, y: 0 }}
className="grid grid-cols-1 gap-12 lg:grid-cols-12"
initial={shouldReduceMotion ? { opacity: 1 } : { opacity: 0, y: 20 }}
transition={
shouldReduceMotion
? { duration: 0 }
: { duration: ANIMATION_DURATION }
}
viewport={{ once: true }}
whileInView={
shouldReduceMotion ? { opacity: 1 } : { opacity: 1, y: 0 }
}
>
{/* Company Info & Newsletter */}
<div className="lg:col-span-5">
<motion.div
animate={
shouldReduceMotion ? { opacity: 1 } : { opacity: 1, y: 0 }
}
initial={
shouldReduceMotion ? { opacity: 1 } : { opacity: 0, y: 20 }
}
transition={
shouldReduceMotion
? { duration: 0 }
: { delay: DELAY_INCREMENT, duration: ANIMATION_DURATION }
}
viewport={{ once: true }}
whileInView={
shouldReduceMotion ? { opacity: 1 } : { opacity: 1, y: 0 }
}
>
<h3 className="mb-4 font-bold text-2xl text-foreground">
{companyName}
</h3>
<p className="mb-8 max-w-md text-foreground/70 text-sm leading-relaxed">
{description}
</p>
{/* Newsletter */}
<div className="mb-8">
<h4 className="mb-2 font-semibold text-foreground text-lg">
{newsletter.title}
</h4>
<p className="mb-4 text-foreground/70 text-sm">
{newsletter.description}
</p>
<div className="flex gap-2">
<input
className="flex-1 rounded-lg border border-border bg-background px-4 py-2 text-sm placeholder:text-foreground/50 focus:border-brand focus:outline-none focus:ring-2 focus:ring-brand/20"
placeholder={newsletter.placeholder}
type="email"
/>
<SmoothButton variant="candy">
{newsletter.buttonText}
</SmoothButton>
</div>
</div>
{/* Social Links */}
<div className="flex gap-4">
{social.twitter ? (
<motion.a
aria-label="Twitter"
className="text-foreground/60 transition-colors hover:text-brand"
href={social.twitter}
rel="noopener noreferrer"
target="_blank"
whileHover={socialHover}
whileTap={socialTap}
>
<svg
aria-hidden="true"
className="h-5 w-5"
fill="currentColor"
viewBox="0 0 24 24"
>
<path d="M23.953 4.57a10 10 0 01-2.825.775 4.958 4.958 0 002.163-2.723c-.951.555-2.005.959-3.127 1.184a4.92 4.92 0 00-8.384 4.482C7.69 8.095 4.067 6.13 1.64 3.162a4.822 4.822 0 00-.666 2.475c0 1.71.87 3.213 2.188 4.096a4.904 4.904 0 01-2.228-.616v.06a4.923 4.923 0 003.946 4.827 4.996 4.996 0 01-2.212.085 4.936 4.936 0 004.604 3.417 9.867 9.867 0 01-6.102 2.105c-.39 0-.779-.023-1.17-.067a13.995 13.995 0 007.557 2.209c9.053 0 13.998-7.496 13.998-13.985 0-.21 0-.42-.015-.63A9.935 9.935 0 0024 4.59z" />
</svg>
<span className="sr-only">Twitter</span>
</motion.a>
) : null}
{social.linkedin ? (
<motion.a
className="text-foreground/60 transition-colors hover:text-brand"
href={social.linkedin}
rel="noopener noreferrer"
target="_blank"
whileHover={socialHover}
whileTap={socialTap}
>
<svg
aria-hidden="true"
className="h-5 w-5"
fill="currentColor"
viewBox="0 0 24 24"
>
<path d="M20.447 20.452h-3.554v-5.569c0-1.328-.027-3.037-1.852-3.037-1.853 0-2.136 1.445-2.136 2.939v5.667H9.351V9h3.414v1.561h.046c.477-.9 1.637-1.85 3.37-1.85 3.601 0 4.267 2.37 4.267 5.455v6.286zM5.337 7.433c-1.144 0-2.063-.926-2.063-2.065 0-1.138.92-2.063 2.063-2.063 1.14 0 2.064.925 2.064 2.063 0 1.139-.925 2.065-2.064 2.065zm1.782 13.019H3.555V9h3.564v11.452zM22.225 0H1.771C.792 0 0 .774 0 1.729v20.542C0 23.227.792 24 1.771 24h20.451C23.2 24 24 23.227 24 22.271V1.729C24 .774 23.2 0 22.222 0h.003z" />
</svg>
<span className="sr-only">LinkedIn</span>
</motion.a>
) : null}
{social.github ? (
<motion.a
className="text-foreground/60 transition-colors hover:text-brand"
href={social.github}
rel="noopener noreferrer"
target="_blank"
whileHover={socialHover}
whileTap={socialTap}
>
<svg
aria-hidden="true"
className="h-5 w-5"
fill="currentColor"
viewBox="0 0 24 24"
>
<path d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z" />
</svg>
<span className="sr-only">GitHub</span>
</motion.a>
) : null}
{social.discord ? (
<motion.a
className="text-foreground/60 transition-colors hover:text-brand"
href={social.discord}
rel="noopener noreferrer"
target="_blank"
whileHover={socialHover}
whileTap={socialTap}
>
<svg
aria-hidden="true"
className="h-5 w-5"
fill="currentColor"
viewBox="0 0 24 24"
>
<path d="M20.317 4.37a19.791 19.791 0 0 0-4.885-1.515.074.074 0 0 0-.079.037c-.21.375-.444.864-.608 1.25a18.27 18.27 0 0 0-5.487 0 12.64 12.64 0 0 0-.617-1.25.077.077 0 0 0-.079-.037A19.736 19.736 0 0 0 3.677 4.37a.07.07 0 0 0-.032.027C.533 9.046-.32 13.58.099 18.057a.082.082 0 0 0 .031.057 19.9 19.9 0 0 0 5.993 3.03.078.078 0 0 0 .084-.028c.462-.63.874-1.295 1.226-1.994a.076.076 0 0 0-.041-.106 13.107 13.107 0 0 1-1.872-.892.077.077 0 0 1-.008-.128 10.2 10.2 0 0 0 .372-.292.074.074 0 0 1 .077-.01c3.928 1.793 8.18 1.793 12.062 0a.074.074 0 0 1 .078.01c.12.098.246.198.373.292a.077.077 0 0 1-.006.127 12.299 12.299 0 0 1-1.873.892.077.077 0 0 0-.041.107c.36.698.772 1.362 1.225 1.993a.076.076 0 0 0 .084.028 19.839 19.839 0 0 0 6.002-3.03.077.077 0 0 0 .032-.054c.5-5.177-.838-9.674-3.549-13.66a.061.061 0 0 0-.031-.03zM8.02 15.33c-1.183 0-2.157-1.085-2.157-2.419 0-1.333.956-2.419 2.157-2.419 1.21 0 2.176 1.096 2.157 2.42 0 1.333-.956 2.418-2.157 2.418zm7.975 0c-1.183 0-2.157-1.085-2.157-2.419 0-1.333.955-2.419 2.157-2.419 1.21 0 2.176 1.096 2.157 2.42 0 1.333-.946 2.418-2.157 2.418z" />
</svg>
<span className="sr-only">Discord</span>
</motion.a>
) : null}
{social.youtube ? (
<motion.a
className="text-foreground/60 transition-colors hover:text-brand"
href={social.youtube}
rel="noopener noreferrer"
target="_blank"
whileHover={socialHover}
whileTap={socialTap}
>
<svg
aria-hidden="true"
className="h-5 w-5"
fill="currentColor"
viewBox="0 0 24 24"
>
<path d="M23.498 6.186a3.016 3.016 0 0 0-2.122-2.136C19.505 3.545 12 3.545 12 3.545s-7.505 0-9.377.505A3.017 3.017 0 0 0 .502 6.186C0 8.07 0 12 0 12s0 3.93.502 5.814a3.016 3.016 0 0 0 2.122 2.136c1.871.505 9.376.505 9.376.505s7.505 0 9.377-.505a3.015 3.015 0 0 0 2.122-2.136C24 15.93 24 12 24 12s0-3.93-.502-5.814zM9.545 15.568V8.432L15.818 12l-6.273 3.568z" />
</svg>
<span className="sr-only">YouTube</span>
</motion.a>
) : null}
</div>
</motion.div>
</div>
{/* Links Grid */}
<div className="grid grid-cols-2 gap-8 lg:col-span-7 lg:grid-cols-4">
{links.product ? (
<motion.div
animate={
shouldReduceMotion ? { opacity: 1 } : { opacity: 1, y: 0 }
}
initial={
shouldReduceMotion ? { opacity: 1 } : { opacity: 0, y: 20 }
}
transition={
shouldReduceMotion
? { duration: 0 }
: { delay: DELAY_PRODUCT, duration: ANIMATION_DURATION }
}
viewport={{ once: true }}
whileInView={
shouldReduceMotion ? { opacity: 1 } : { opacity: 1, y: 0 }
}
>
<h4 className="mb-4 font-semibold text-foreground text-sm uppercase tracking-wide">
Product
</h4>
<ul className="space-y-3">
{links.product.map((link) => (
<li key={link.name}>
<a
className="text-foreground/70 text-sm transition-colors hover:text-brand"
href={link.url}
>
{link.name}
</a>
</li>
))}
</ul>
</motion.div>
) : null}
{links.company ? (
<motion.div
animate={
shouldReduceMotion ? { opacity: 1 } : { opacity: 1, y: 0 }
}
initial={
shouldReduceMotion ? { opacity: 1 } : { opacity: 0, y: 20 }
}
transition={
shouldReduceMotion
? { duration: 0 }
: { delay: DELAY_COMPANY, duration: ANIMATION_DURATION }
}
viewport={{ once: true }}
whileInView={
shouldReduceMotion ? { opacity: 1 } : { opacity: 1, y: 0 }
}
>
<h4 className="mb-4 font-semibold text-foreground text-sm uppercase tracking-wide">
Company
</h4>
<ul className="space-y-3">
{links.company.map((link) => (
<li key={link.name}>
<a
className="text-foreground/70 text-sm transition-colors hover:text-brand"
href={link.url}
>
{link.name}
</a>
</li>
))}
</ul>
</motion.div>
) : null}
{links.support ? (
<motion.div
animate={
shouldReduceMotion ? { opacity: 1 } : { opacity: 1, y: 0 }
}
initial={
shouldReduceMotion ? { opacity: 1 } : { opacity: 0, y: 20 }
}
transition={
shouldReduceMotion
? { duration: 0 }
: { delay: DELAY_SUPPORT, duration: ANIMATION_DURATION }
}
viewport={{ once: true }}
whileInView={
shouldReduceMotion ? { opacity: 1 } : { opacity: 1, y: 0 }
}
>
<h4 className="mb-4 font-semibold text-foreground text-sm uppercase tracking-wide">
Support
</h4>
<ul className="space-y-3">
{links.support.map((link) => (
<li key={link.name}>
<a
className="text-foreground/70 text-sm transition-colors hover:text-brand"
href={link.url}
>
{link.name}
</a>
</li>
))}
</ul>
</motion.div>
) : null}
{links.legal ? (
<motion.div
animate={
shouldReduceMotion ? { opacity: 1 } : { opacity: 1, y: 0 }
}
initial={
shouldReduceMotion ? { opacity: 1 } : { opacity: 0, y: 20 }
}
transition={
shouldReduceMotion
? { duration: 0 }
: { delay: DELAY_LEGAL, duration: ANIMATION_DURATION }
}
viewport={{ once: true }}
whileInView={
shouldReduceMotion ? { opacity: 1 } : { opacity: 1, y: 0 }
}
>
<h4 className="mb-4 font-semibold text-foreground text-sm uppercase tracking-wide">
Legal
</h4>
<ul className="space-y-3">
{links.legal.map((link) => (
<li key={link.name}>
<a
className="text-foreground/70 text-sm transition-colors hover:text-brand"
href={link.url}
>
{link.name}
</a>
</li>
))}
</ul>
</motion.div>
) : null}
</div>
</motion.div>
{/* Copyright */}
<motion.div
animate={{ opacity: 1, y: 0 }}
className="mt-12 border-border border-t pt-8 text-center"
initial={{ opacity: 0, y: 20 }}
transition={{
delay: DELAY_COPYRIGHT,
duration: ANIMATION_DURATION,
}}
viewport={{ once: true }}
whileInView={{ opacity: 1, y: 0 }}
>
<p className="text-foreground/60 text-sm">{copyright}</p>
</motion.div>
</div>
</footer>
);
}
export default FooterComplex;