Footer
blockmarketingSite footer collection: multi-column newsletter, oversized brand statement, dense developer grid, mega newsletter, single-row minimal, and dense sitemap variants — each spelled out inline so copy lives in one place.
Installation
$pnpm dlx shadcn-vue@latest add https://uipkge.dev/r/vue/footer.json$npx shadcn-vue@latest add https://uipkge.dev/r/vue/footer.json$yarn dlx shadcn-vue@latest add https://uipkge.dev/r/vue/footer.json$bunx shadcn-vue@latest add https://uipkge.dev/r/vue/footer.jsonNamed registry:
npx shadcn-vue@latest add @uipkge/footerInstalls to:app/components/blocks/footer/Variants
Loading interactive previews…
Loading interactive previews…
Schema
Type aliases exported from this item's source. Use these to shape the data you pass in.
FooterColumninterface FooterColumn {
title: string
links: { label: string; href: string; badge?: string }[]
}Files installed (6)
app/components/blocks/footer/Footer01.vue5.4 kB
<script setup lang="ts"> import { ref } from 'vue' import { Boxes, Github, Linkedin, Twitter } from 'lucide-vue-next' import { Button } from '@/components/ui/button' import { Input } from '@/components/ui/input' import { Separator } from '@/components/ui/separator' const newsletter = ref('') const subscribed = ref(false) function subscribe() { if (!newsletter.value) return subscribed.value = true newsletter.value = '' } </script> <template> <footer data-slot="footer-01" class="bg-background border-t"> <div class="mx-auto max-w-6xl px-6 py-16"> <div class="grid gap-10 lg:grid-cols-12"> <div class="space-y-4 lg:col-span-4"> <div class="flex items-center gap-2"> <div class="bg-primary text-primary-foreground flex size-8 items-center justify-center rounded-md"> <Boxes class="size-4" /> </div> <span class="text-base font-semibold">Acme</span> </div> <p class="text-muted-foreground max-w-sm text-sm">Built on shadcn-vue. Get product updates monthly.</p> <form class="flex max-w-sm gap-2" @submit.prevent="subscribe"> <Input v-model="newsletter" type="email" placeholder="you@company.com" required class="flex-1" /> <Button type="submit">Subscribe</Button> </form> <p v-if="subscribed" class="text-success text-xs">Thanks — check your inbox to confirm.</p> </div> <div class="grid grid-cols-2 gap-6 sm:grid-cols-4 lg:col-span-8"> <div class="space-y-3"> <h3 class="text-sm font-semibold">Product</h3> <ul class="space-y-2"> <li> <a href="#" class="text-muted-foreground hover:text-foreground text-sm transition-colors">Features</a> </li> <li> <a href="#" class="text-muted-foreground hover:text-foreground text-sm transition-colors">Pricing</a> </li> <li> <a href="#" class="text-muted-foreground hover:text-foreground text-sm transition-colors">Changelog</a> </li> <li> <a href="#" class="text-muted-foreground hover:text-foreground text-sm transition-colors">Roadmap</a> </li> </ul> </div> <div class="space-y-3"> <h3 class="text-sm font-semibold">Company</h3> <ul class="space-y-2"> <li> <a href="#" class="text-muted-foreground hover:text-foreground text-sm transition-colors">About</a> </li> <li> <a href="#" class="text-muted-foreground hover:text-foreground text-sm transition-colors">Blog</a> </li> <li> <a href="#" class="text-muted-foreground hover:text-foreground text-sm transition-colors">Careers</a> </li> <li> <a href="#" class="text-muted-foreground hover:text-foreground text-sm transition-colors">Contact</a> </li> </ul> </div> <div class="space-y-3"> <h3 class="text-sm font-semibold">Resources</h3> <ul class="space-y-2"> <li> <a href="#" class="text-muted-foreground hover:text-foreground text-sm transition-colors" >Documentation</a > </li> <li><a href="#" class="text-muted-foreground hover:text-foreground text-sm transition-colors">API</a></li> <li> <a href="#" class="text-muted-foreground hover:text-foreground text-sm transition-colors" >Help center</a > </li> <li> <a href="#" class="text-muted-foreground hover:text-foreground text-sm transition-colors">Status</a> </li> </ul> </div> <div class="space-y-3"> <h3 class="text-sm font-semibold">Legal</h3> <ul class="space-y-2"> <li> <a href="#" class="text-muted-foreground hover:text-foreground text-sm transition-colors">Terms</a> </li> <li> <a href="#" class="text-muted-foreground hover:text-foreground text-sm transition-colors">Privacy</a> </li> <li> <a href="#" class="text-muted-foreground hover:text-foreground text-sm transition-colors">Security</a> </li> <li><a href="#" class="text-muted-foreground hover:text-foreground text-sm transition-colors">DPA</a></li> </ul> </div> </div> </div> <Separator class="my-10" /> <div class="flex flex-wrap items-center justify-between gap-4"> <p class="text-muted-foreground text-xs">© 2026 Acme. All rights reserved.</p> <div class="flex items-center gap-3"> <a href="#" aria-label="GitHub" class="text-muted-foreground hover:text-foreground transition-colors"> <Github class="size-4" /> </a> <a href="#" aria-label="Twitter" class="text-muted-foreground hover:text-foreground transition-colors"> <Twitter class="size-4" /> </a> <a href="#" aria-label="LinkedIn" class="text-muted-foreground hover:text-foreground transition-colors"> <Linkedin class="size-4" /> </a> </div> </div> </div> </footer> </template>app/components/blocks/footer/FooterBrandStatement.vue3 kB
<script setup lang="ts"> import { Github, Layers, Linkedin } from 'lucide-vue-next' import { Badge } from '@/components/ui/badge' import { Button } from '@/components/ui/button' import { Separator } from '@/components/ui/separator' const sections = [ { heading: 'Platform', links: ['Metric layer', 'Governance', 'Alerting', 'Embedding'] }, { heading: 'Company', links: ['About', 'Careers', 'Customers', 'Press kit'] }, { heading: 'Resources', links: ['Documentation', 'Guides', 'Benchmarks', 'Status'] }, ] </script> <template> <footer data-slot="footer-brand-statement" class="border-border bg-background border-t"> <div class="mx-auto max-w-6xl px-6 py-16"> <a href="#top" class="flex w-fit items-center gap-2 font-semibold tracking-tight"> <Layers class="text-primary size-5" aria-hidden="true" /> Northwind </a> <!-- The statement is the only oversized type on the page. Keep it to one sentence; at this size a second one stops being read. --> <p class="mt-8 max-w-3xl text-2xl leading-tight font-medium tracking-tight text-balance sm:text-3xl"> Every number your company acts on should have one definition, one owner, and a history you can read. </p> <p class="text-muted-foreground mt-4 max-w-xl text-sm leading-relaxed"> That is the whole product. Everything else here is detail. </p> <Separator class="my-12" /> <div class="grid grid-cols-2 gap-8 sm:grid-cols-3"> <div v-for="section in sections" :key="section.heading"> <p class="text-muted-foreground/70 text-xs font-medium tracking-wide uppercase">{{ section.heading }}</p> <ul class="mt-3 space-y-2"> <li v-for="link in section.links" :key="link"> <a href="#" class="text-muted-foreground hover:text-foreground text-sm transition-colors">{{ link }}</a> </li> </ul> </div> </div> <Separator class="my-10" /> <div class="flex flex-col gap-4 sm:flex-row sm:items-center"> <div class="text-muted-foreground flex flex-wrap items-center gap-x-4 gap-y-2 text-xs"> <span>© 2026 Northwind Data, Inc.</span> <a href="#privacy" class="hover:text-foreground transition-colors">Privacy</a> <a href="#terms" class="hover:text-foreground transition-colors">Terms</a> <a href="mailto:hello@northwind.example" class="hover:text-foreground transition-colors"> hello@northwind.example </a> </div> <div class="flex items-center gap-2 sm:ml-auto"> <Badge variant="outline">SOC 2 Type II</Badge> <Badge variant="outline">GDPR</Badge> <Button variant="ghost" size="icon" class="size-8" aria-label="GitHub"> <Github class="size-4" aria-hidden="true" /> </Button> <Button variant="ghost" size="icon" class="size-8" aria-label="LinkedIn"> <Linkedin class="size-4" aria-hidden="true" /> </Button> </div> </div> </div> </footer> </template>app/components/blocks/footer/FooterDeveloperDenseGrid.vue5.2 kB
<script setup lang="ts"> import { Github, Globe, Twitter } from 'lucide-vue-next' import { Button } from '@/components/ui/button' interface FooterColumn { title: string links: { label: string; href: string; badge?: string }[] } const columns: FooterColumn[] = [ { title: 'UI Primitives', links: [ { label: 'Button & Controls', href: '#' }, { label: 'Dialog & Overlays', href: '#' }, { label: 'Data Table & Virtual', href: '#' }, { label: 'Charts & Sparklines', href: '#' }, { label: 'Form Controls & Inputs', href: '#' }, { label: 'KpiGrid & Metric Tiles', href: '#' }, ], }, { title: 'Marketing Blocks', links: [ { label: 'Hero Sandboxes', href: '#' }, { label: 'Feature Workbenches', href: '#' }, { label: 'Pricing Matrix & ROI', href: '#' }, { label: 'Social Proof Tickers', href: '#' }, { label: 'Testimonial Masonry', href: '#' }, { label: 'Interactive Demos', href: '#', badge: 'New' }, ], }, { title: 'Ecosystem Templates', links: [ { label: 'HRMS Nuxt Template', href: '#' }, { label: 'HMS Clinical Dashboard', href: '#' }, { label: 'Shipment Tracking Live', href: '#' }, { label: 'Fintech Banking Portal', href: '#' }, { label: 'AI LLM Workspace', href: '#' }, ], }, { title: 'Developer DX', links: [ { label: 'CLI Installation Guide', href: '#' }, { label: 'Tailwind CSS v4 Tokens', href: '#' }, { label: 'Dual-Framework Parity', href: '#' }, { label: 'Reka UI Primitives', href: '#' }, { label: 'Radix UI Primitives', href: '#' }, { label: 'Changelog Timeline', href: '#' }, ], }, ] </script> <template> <footer data-slot="footer-developer-dense-grid" class="border-border bg-card text-foreground border-t"> <div class="mx-auto max-w-7xl space-y-12 px-4 py-12 sm:px-6 sm:py-16 lg:px-8"> <!-- Top Grid: Brand & Categorized Columns --> <div class="grid grid-cols-2 gap-8 text-left md:grid-cols-6 lg:grid-cols-12"> <!-- Brand Info (4 Cols) --> <div class="col-span-2 space-y-4 md:col-span-6 lg:col-span-4"> <div class="flex items-center gap-2 font-mono text-base font-bold tracking-tight"> <span class="bg-primary text-primary-foreground flex size-6 items-center justify-center rounded-lg text-xs font-bold shadow-xs" > U </span> <span>UIPKGE.DEV</span> </div> <p class="text-muted-foreground max-w-sm text-xs leading-relaxed"> The open-source unbundled component registry where you own 100% of the source code. Calibrated tokens, pure AST distribution, and mathematical dual-framework parity across Vue 3.5 and React 19. </p> <!-- Social links --> <div class="flex items-center gap-2 pt-2"> <Button size="sm" variant="outline" class="size-8 rounded-lg p-0"> <Github class="size-3.5" /> </Button> <Button size="sm" variant="outline" class="size-8 rounded-lg p-0"> <Twitter class="size-3.5" /> </Button> <Button size="sm" variant="outline" class="size-8 rounded-lg p-0"> <Globe class="size-3.5" /> </Button> </div> </div> <!-- 4 Links Columns (8 Cols total, 2 cols each) --> <div v-for="(col, idx) in columns" :key="idx" class="col-span-1 space-y-3 md:col-span-3 lg:col-span-2"> <h4 class="text-foreground font-mono text-xs font-bold tracking-wider uppercase"> {{ col.title }} </h4> <ul class="text-muted-foreground space-y-2 font-mono text-xs"> <li v-for="(link, lIdx) in col.links" :key="lIdx"> <a :href="link.href" class="hover:text-foreground inline-flex items-center gap-1.5 transition-colors"> <span>{{ link.label }}</span> <span v-if="link.badge" class="py-0.2 bg-primary/20 text-primary rounded px-1.5 text-xs font-bold uppercase" > {{ link.badge }} </span> </a> </li> </ul> </div> </div> <!-- Bottom Operational Status & Copyright Bar --> <div class="border-border/80 text-muted-foreground flex flex-col items-center justify-between gap-4 border-t pt-8 text-center font-mono text-xs sm:flex-row sm:text-left" > <!-- Live System Telemetry Status --> <div class="flex items-center gap-2.5"> <span class="relative flex size-2"> <span class="bg-success absolute inline-flex h-full w-full rounded-full opacity-75" /> <span class="bg-success relative inline-flex size-2 rounded-full" /> </span> <span class="text-foreground font-medium">All Global Edge Nodes Operational</span> <span class="text-muted-foreground/60">• Latency: 14ms (P99)</span> </div> <!-- Right Copyright & MIT Notice --> <div class="flex items-center gap-3"> <span>MIT Licensed • Unbundled Code</span> <span>© {{ new Date().getFullYear() }} UIPKGE</span> </div> </div> </div> </footer> </template>app/components/blocks/footer/FooterMegaNewsletter.vue4 kB
<script setup lang="ts"> import { ref } from 'vue' import { ArrowRight, Check, Github, Layers, Linkedin, Youtube } from 'lucide-vue-next' import { Badge } from '@/components/ui/badge' import { Button } from '@/components/ui/button' import { Input } from '@/components/ui/input' import { Separator } from '@/components/ui/separator' const sections = [ { heading: 'Product', links: ['Metric layer', 'Dashboards', 'Alerting', 'Embedding', 'Changelog'] }, { heading: 'Solutions', links: ['Finance', 'RevOps', 'Analytics', 'Engineering'] }, { heading: 'Developers', links: ['Documentation', 'API reference', 'CLI', 'Status'] }, { heading: 'Company', links: ['About', 'Careers', 'Customers', 'Press kit'] }, { heading: 'Legal', links: ['Privacy', 'Terms', 'DPA', 'Subprocessors'] }, ] const email = ref('') const submitted = ref(false) function subscribe() { if (!email.value.trim()) return submitted.value = true } </script> <template> <footer data-slot="footer-mega-newsletter" class="border-border bg-background border-t"> <div class="mx-auto max-w-6xl px-6 py-14"> <div class="grid gap-12 lg:grid-cols-[22rem_1fr] lg:gap-16"> <div> <a href="#top" class="flex items-center gap-2 font-semibold tracking-tight"> <Layers class="text-primary size-5" aria-hidden="true" /> Northwind </a> <p class="text-muted-foreground mt-3 max-w-sm text-sm leading-relaxed"> One certified metric layer between your warehouse and everything downstream. </p> <form class="mt-6" @submit.prevent="subscribe"> <label for="footer-email" class="text-sm font-medium">Monthly engineering notes</label> <div class="mt-2 flex gap-2"> <Input id="footer-email" v-model="email" type="email" placeholder="you@company.com" autocomplete="email" class="max-w-64" /> <Button type="submit"> Subscribe <ArrowRight class="ml-1.5 size-3.5" aria-hidden="true" /> </Button> </div> <!-- Status line reserves its own row, so confirming does not shift the columns. --> <p class="text-muted-foreground mt-2 min-h-5 text-xs"> <span v-if="submitted" class="text-success inline-flex items-center gap-1.5"> <Check class="size-3" aria-hidden="true" /> Check your inbox to confirm. </span> <span v-else>One email a month. Unsubscribe in one click.</span> </p> </form> </div> <div class="grid grid-cols-2 gap-8 sm:grid-cols-3 lg:grid-cols-5"> <div v-for="section in sections" :key="section.heading"> <p class="text-muted-foreground/70 text-xs font-medium tracking-wide uppercase">{{ section.heading }}</p> <ul class="mt-3 space-y-2"> <li v-for="link in section.links" :key="link"> <a href="#" class="text-muted-foreground hover:text-foreground text-sm transition-colors">{{ link }}</a> </li> </ul> </div> </div> </div> <Separator class="my-10" /> <div class="flex flex-col gap-4 sm:flex-row sm:items-center"> <p class="text-muted-foreground text-xs">© 2026 Northwind Data, Inc. All rights reserved.</p> <Badge variant="outline" class="w-fit">SOC 2 Type II</Badge> <div class="flex items-center gap-1 sm:ml-auto"> <Button variant="ghost" size="icon" class="size-8" aria-label="GitHub"> <Github class="size-4" aria-hidden="true" /> </Button> <Button variant="ghost" size="icon" class="size-8" aria-label="LinkedIn"> <Linkedin class="size-4" aria-hidden="true" /> </Button> <Button variant="ghost" size="icon" class="size-8" aria-label="YouTube"> <Youtube class="size-4" aria-hidden="true" /> </Button> </div> </div> </div> </footer> </template>app/components/blocks/footer/FooterMinimalRow.vue1.7 kB
<script setup lang="ts"> import { Github, Layers, Linkedin } from 'lucide-vue-next' import { Button } from '@/components/ui/button' import { Separator } from '@/components/ui/separator' const links = [ { label: 'Product', href: '#product' }, { label: 'Pricing', href: '#pricing' }, { label: 'Docs', href: '#docs' }, { label: 'Privacy', href: '#privacy' }, { label: 'Terms', href: '#terms' }, ] </script> <template> <footer data-slot="footer-minimal-row" class="border-border bg-background border-t"> <div class="mx-auto flex max-w-6xl flex-col gap-4 px-6 py-6 sm:flex-row sm:items-center"> <a href="#top" class="flex shrink-0 items-center gap-2 text-sm font-semibold tracking-tight"> <Layers class="text-primary size-4" aria-hidden="true" /> Northwind </a> <Separator orientation="vertical" class="hidden h-4 sm:block" /> <nav class="flex flex-wrap items-center gap-x-5 gap-y-2" aria-label="Footer"> <a v-for="link in links" :key="link.href" :href="link.href" class="text-muted-foreground hover:text-foreground text-sm transition-colors" > {{ link.label }} </a> </nav> <div class="flex items-center gap-1 sm:ml-auto"> <Button variant="ghost" size="icon" class="size-8" aria-label="GitHub"> <Github class="size-4" aria-hidden="true" /> </Button> <Button variant="ghost" size="icon" class="size-8" aria-label="LinkedIn"> <Linkedin class="size-4" aria-hidden="true" /> </Button> <span class="text-muted-foreground ml-2 text-xs">© 2026 Northwind</span> </div> </div> </footer> </template>app/components/blocks/footer/FooterSitemapDense.vue4 kB
<script setup lang="ts"> import { ref } from 'vue' import { Github, Layers, Linkedin } from 'lucide-vue-next' import { Badge } from '@/components/ui/badge' import { Button } from '@/components/ui/button' import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select' import { Separator } from '@/components/ui/separator' const sections = [ { heading: 'Platform', links: ['Metric layer', 'Dashboards', 'Alerting', 'Embedding', 'API', 'CLI'] }, { heading: 'Solutions', links: ['Finance', 'RevOps', 'Analytics', 'Engineering', 'Healthcare', 'Retail'] }, { heading: 'Developers', links: ['Documentation', 'API reference', 'SDKs', 'Webhooks', 'Status', 'Changelog'] }, { heading: 'Resources', links: ['Guides', 'Benchmarks', 'Customers', 'Blog', 'Webinars', 'Glossary'] }, { heading: 'Company', links: ['About', 'Careers', 'Press kit', 'Partners', 'Contact', 'Security'] }, { heading: 'Legal', links: ['Privacy', 'Terms', 'DPA', 'Subprocessors', 'SLA', 'Accessibility'] }, ] const region = ref('eu') const currency = ref('eur') </script> <template> <footer data-slot="footer-sitemap-dense" class="border-border bg-background border-t"> <div class="mx-auto max-w-6xl px-6 py-12"> <div class="grid grid-cols-2 gap-x-6 gap-y-8 sm:grid-cols-3 lg:grid-cols-6"> <div v-for="section in sections" :key="section.heading"> <p class="text-muted-foreground/70 text-xs font-medium tracking-wide uppercase">{{ section.heading }}</p> <ul class="mt-3 space-y-1.5"> <li v-for="link in section.links" :key="link"> <a href="#" class="text-muted-foreground hover:text-foreground text-sm transition-colors">{{ link }}</a> </li> </ul> </div> </div> <Separator class="my-10" /> <div class="flex flex-col gap-6 lg:flex-row lg:items-center"> <a href="#top" class="flex shrink-0 items-center gap-2 text-sm font-semibold tracking-tight"> <Layers class="text-primary size-4" aria-hidden="true" /> Northwind </a> <div class="flex flex-wrap items-center gap-2"> <Select v-model="region"> <SelectTrigger class="h-8 w-40 text-xs" aria-label="Data region"> <SelectValue /> </SelectTrigger> <SelectContent> <SelectItem value="eu">EU (Frankfurt)</SelectItem> <SelectItem value="us">US (Virginia)</SelectItem> <SelectItem value="au">AU (Sydney)</SelectItem> </SelectContent> </Select> <Select v-model="currency"> <SelectTrigger class="h-8 w-28 text-xs" aria-label="Currency"> <SelectValue /> </SelectTrigger> <SelectContent> <SelectItem value="eur">EUR €</SelectItem> <SelectItem value="usd">USD $</SelectItem> <SelectItem value="gbp">GBP £</SelectItem> </SelectContent> </Select> </div> <div class="flex flex-wrap items-center gap-3 lg:ml-auto"> <!-- Status dot is a live indicator, so it carries its own text for screen readers rather than relying on colour alone. --> <a href="#status" class="hover:bg-muted flex items-center gap-2 rounded-md px-2 py-1 transition-colors"> <span class="bg-success size-2 rounded-full" aria-hidden="true" /> <span class="text-muted-foreground text-xs">All systems operational</span> </a> <Badge variant="outline">SOC 2</Badge> <Badge variant="outline">GDPR</Badge> <Button variant="ghost" size="icon" class="size-8" aria-label="GitHub"> <Github class="size-4" aria-hidden="true" /> </Button> <Button variant="ghost" size="icon" class="size-8" aria-label="LinkedIn"> <Linkedin class="size-4" aria-hidden="true" /> </Button> </div> </div> <p class="text-muted-foreground mt-6 text-xs">© 2026 Northwind Data, Inc. All rights reserved.</p> </div> </footer> </template>
Raw manifest:https://uipkge.dev/r/vue/footer.json