blocktemplate
Agency site
A studio site: overlay hero, work grid, process steps, a featured quote and the team.
Preview
Installation
pnpm dlx shadcn@latest add https://facadeui.dev/r/agency.jsonPulls in card-list, cta-band, footer, hero-with-media, logo-cloud, logo-mark, nav-top, stat, stats, steps, team, testimonial, testimonial-single, types. The CLI installs them for you.
Usage
The exact source of the preview above.
"use client"
/**
* `"use client"` because the process steps carry icon components and NavTop is
* a client component — icons cannot cross a server-to-client boundary as props.
*/
import { Agency } from "@registry/templates/agency"
import {
FOOTER_GROUPS,
LOGOS,
NAV_ITEMS,
POSTS,
QUOTES,
STATS,
STEPS,
TEAM,
} from "./content"
export function Demo() {
return (
<div className="-m-6 sm:-m-10">
<Agency
content={{
brand: "Facade Studio",
nav: NAV_ITEMS,
navActions: [{ label: "Start a project", href: "#contact" }],
hero: {
eyebrow: "Design and engineering",
title: "We build the front of the business",
description:
"Marketing sites, design systems and the handover that makes them last.",
actions: [
{ label: "See our work", href: "#work" },
{ label: "Start a project", href: "#contact" },
],
// Decorative: it says nothing the copy does not.
media: (
<div
role="presentation"
className="size-full bg-[radial-gradient(circle_at_25%_25%,#2f3d5c,transparent_55%),radial-gradient(circle_at_75%_35%,#5c3350,transparent_50%),linear-gradient(150deg,#12151d,#2b2340)]"
/>
),
},
clients: { title: "Selected clients", items: LOGOS },
work: {
eyebrow: "Work",
title: "Recent projects",
description: "A few of the sites and systems we have shipped.",
items: POSTS,
},
process: {
eyebrow: "Process",
title: "How we work",
items: STEPS,
},
quote: { title: "Client story", item: QUOTES[0]! },
stats: {
eyebrow: "Track record",
title: "In numbers",
items: STATS.slice(0, 3),
},
team: { eyebrow: "Studio", title: "Who you will work with", items: TEAM },
cta: {
title: "Have something in mind?",
description:
"Tell us what you are building and we will tell you how we would approach it.",
actions: [{ label: "Start a project", href: "#contact" }],
},
footer: {
groups: FOOTER_GROUPS.slice(0, 2),
copyright: "© 2026 Facade Studio",
legal: [{ label: "Privacy", href: "#privacy" }],
},
}}
/>
</div>
)
}Source
What the CLI copies into your project, byte for byte.
/**
* Agency — a studio or agency site, assembled from registry sections.
*
* Differs from `SaasLanding` in shape rather than in parts: a centred hero over
* media, work as a card grid, a process as numbered steps, the people, and one
* featured quote instead of a wall of them. Agencies sell judgement and people,
* so the page leads with work and faces rather than with features and pricing.
*
* a11y: the same explicit outline discipline — the hero is the page's `<h1>`,
* every band an `<h2>`, every item an `<h3>`. The hero uses the `overlay`
* placement, which always paints a scrim, because text over an arbitrary image
* has no guaranteed contrast ratio.
*
* Dependencies: react, @/lib/types, @/components/sections/*.
*/
import type { ReactNode } from "react"
import type { CtaItem, ImageComponent, LinkComponent } from "@/lib/types"
import { CardList, type CardItem } from "@/components/sections/card-list"
import { CtaBand } from "@/components/sections/cta-band"
import { Footer, type FooterGroup, type FooterLink } from "@/components/sections/footer"
import { HeroWithMedia } from "@/components/sections/hero-with-media"
import { LogoCloud } from "@/components/sections/logo-cloud"
import { NavTop, type NavItem } from "@/components/sections/nav-top"
import { Stats } from "@/components/sections/stats"
import { Steps, type StepItem } from "@/components/sections/steps"
import { Team, type TeamMember } from "@/components/sections/team"
import { TestimonialSingle } from "@/components/sections/testimonial-single"
import type { LogoItem } from "@/components/ui/logo-mark"
import type { StatItem } from "@/components/ui/stat"
import type { TestimonialItem } from "@/components/ui/testimonial"
export interface AgencyContent {
brand: ReactNode
nav: NavItem[]
navActions?: CtaItem[]
hero: {
eyebrow?: string
title: string
description: string
actions: CtaItem[]
/** Decorative in overlay placement: give it `alt=""`. */
media: ReactNode
}
clients?: { title?: string; items: LogoItem[] }
work: {
eyebrow?: string
title: string
description?: string
items: CardItem[]
}
process?: {
eyebrow?: string
title: string
description?: string
items: StepItem[]
}
stats?: { title?: string; eyebrow?: string; items: StatItem[] }
quote?: { title?: string; item: TestimonialItem }
team?: {
eyebrow?: string
title: string
description?: string
items: TeamMember[]
}
cta: {
eyebrow?: string
title: string
description?: string
actions: CtaItem[]
}
footer: {
groups?: FooterGroup[]
brand?: ReactNode
copyright?: ReactNode
legal?: FooterLink[]
}
}
export interface AgencyProps {
content: AgencyContent
image?: ImageComponent
link?: LinkComponent
currentPath?: string
banner?: ReactNode
}
export function Agency({ content, image, link, currentPath, banner }: AgencyProps) {
const { hero, clients, work, process, stats, quote, team, cta, footer } = content
return (
<>
{banner}
<NavTop
brand={content.brand}
items={content.nav}
actions={content.navActions}
link={link}
currentPath={currentPath}
/>
<main id="main">
<HeroWithMedia
eyebrow={hero.eyebrow}
title={hero.title}
description={hero.description}
actions={hero.actions}
media={hero.media}
link={link}
placement="overlay"
size="xl"
headingLevel={1}
spacing="lg"
/>
{clients ? (
<LogoCloud
title={clients.title}
items={clients.items}
image={image}
link={link}
headingLevel={2}
spacing="sm"
/>
) : null}
<CardList
eyebrow={work.eyebrow}
title={work.title}
description={work.description}
items={work.items}
image={image}
link={link}
headingLevel={2}
itemHeadingLevel={3}
columns={3}
/>
{process ? (
<Steps
eyebrow={process.eyebrow}
title={process.title}
description={process.description}
items={process.items}
headingLevel={2}
itemHeadingLevel={3}
/>
) : null}
{quote ? (
<TestimonialSingle
title={quote.title ?? "Client story"}
item={quote.item}
image={image}
variant="muted"
/>
) : null}
{stats ? (
<Stats
eyebrow={stats.eyebrow}
title={stats.title}
items={stats.items}
headingLevel={2}
variant="divided"
className="border-y"
/>
) : null}
{team ? (
<Team
eyebrow={team.eyebrow}
title={team.title}
description={team.description}
items={team.items}
image={image}
link={link}
headingLevel={2}
itemHeadingLevel={3}
shape="circle"
/>
) : null}
<CtaBand
eyebrow={cta.eyebrow}
title={cta.title}
description={cta.description}
actions={cta.actions}
link={link}
headingLevel={2}
layout="split"
variant="card"
/>
</main>
<Footer
brand={footer.brand ?? content.brand}
groups={footer.groups}
copyright={footer.copyright}
legal={footer.legal}
link={link}
headingLevel={2}
/>
</>
)
}Props
AgencyProps
| Prop | Type | Default |
|---|---|---|
content*Required | AgencyContent | — |
image | ImageComponent | — |
link | LinkComponent | — |
currentPath | string | — |
banner | ReactNode | — |