blocktemplate
SaaS landing page
A complete SaaS marketing page assembled from registry sections, driven by one content object.
Preview
Installation
pnpm dlx shadcn@latest add https://facadeui.dev/r/saas-landing.jsonPulls in cta-band, faq-accordion, feature-grid, feature-rows, footer, hero-split, logo-cloud, logo-mark, nav-top, pricing-tier, pricing-tiers, stat, stats, testimonial, testimonials-grid, types. The CLI installs them for you.
Usage
The exact source of the preview above.
"use client"
/**
* `"use client"` because the template's feature grid carries icon components,
* and NavTop below it is a client component — icons cannot cross a
* server-to-client boundary as props. See the note on `IconComponent`.
*/
import { SaasLanding } from "@registry/templates/saas-landing"
import {
ACTIONS,
FAQS,
FEATURES,
FOOTER_GROUPS,
LOGOS,
MediaPlaceholder,
NAV_ITEMS,
PLANS,
QUOTES,
ROWS,
STATS,
} from "./content"
export function Demo() {
return (
<div className="-m-6 sm:-m-10">
<SaasLanding
currentPath="#pricing"
content={{
brand: "Facade UI",
nav: NAV_ITEMS,
navActions: [
{ label: "Sign in", href: "#sign-in", variant: "ghost" },
{ label: "Start free", href: "#start" },
],
hero: {
eyebrow: "Free and open source",
title: "The front of every great website",
description:
"Marketing sections and a design system you install with the shadcn CLI. The source lands in your project — yours to read, change and own.",
actions: ACTIONS,
note: "MIT licensed. No account, no gating.",
media: <MediaPlaceholder label="Product screenshot" />,
},
logos: { title: "Trusted by teams shipping every day", items: LOGOS },
features: {
eyebrow: "Features",
title: "Everything a marketing page needs",
description: "Composable sections that take typed content and slots.",
items: FEATURES,
},
rows: {
eyebrow: "How it works",
title: "Install a piece, own the source",
items: ROWS,
},
stats: {
eyebrow: "By the numbers",
title: "What the checks cover",
items: STATS,
},
testimonials: {
eyebrow: "Customers",
title: "What teams say",
items: QUOTES.slice(0, 3),
},
pricing: {
eyebrow: "Pricing",
title: "Simple, and actually free",
items: PLANS,
note: "All prices exclude VAT.",
},
faq: { eyebrow: "FAQ", title: "Questions, answered", items: FAQS },
cta: {
title: "Ready when you are",
description:
"Install the first section and see how it sits in your own theme.",
actions: [{ label: "Get started", href: "#start" }],
},
footer: {
groups: FOOTER_GROUPS,
copyright: "© 2026 Facade UI contributors",
legal: [
{ label: "Privacy", href: "#privacy" },
{ label: "Terms", href: "#terms" },
],
},
}}
/>
</div>
)
}Source
What the CLI copies into your project, byte for byte.
/**
* SaasLanding — a complete SaaS marketing page, assembled from registry sections.
*
* A template is composition, not a new component: every section below is the
* same one you can install on its own, given content. That is the point — the
* template shows how the pieces sit together and is meant to be edited, not
* configured.
*
* Everything is content-driven through one `SaasLandingContent` object, so the
* page can be rendered from a CMS payload without touching this file. Pass
* `image` and `link` once and they reach every section that needs them.
*
* a11y: the heading outline is the template's job, and it is explicit here
* rather than left to each section's default. The hero is the page's `<h1>`;
* every other band is an `<h2>` with its own items at `<h3>`. Because the
* sections take `headingLevel` as a prop, that holds no matter how the page is
* rearranged.
*
* `<main>` is rendered here, and `NavTop`'s `<header>` and `Footer`'s
* `<footer>` sit outside it, so the landmark structure is correct for the whole
* document rather than for a fragment of it.
*
* Dependencies: react, @/lib/types, @/components/sections/*.
*/
import type { ReactNode } from "react"
import type { CtaItem, ImageComponent, LinkComponent } from "@/lib/types"
import { CtaBand } from "@/components/sections/cta-band"
import { FaqAccordion, type FaqItem } from "@/components/sections/faq-accordion"
import { FeatureGrid, type FeatureItem } from "@/components/sections/feature-grid"
import { FeatureRows, type FeatureRowItem } from "@/components/sections/feature-rows"
import { Footer, type FooterGroup, type FooterLink } from "@/components/sections/footer"
import { HeroSplit } from "@/components/sections/hero-split"
import { LogoCloud } from "@/components/sections/logo-cloud"
import { NavTop, type NavItem } from "@/components/sections/nav-top"
import { PricingTiers } from "@/components/sections/pricing-tiers"
import { Stats } from "@/components/sections/stats"
import { TestimonialsGrid } from "@/components/sections/testimonials-grid"
import type { LogoItem } from "@/components/ui/logo-mark"
import type { PricingTierItem } from "@/components/ui/pricing-tier"
import type { StatItem } from "@/components/ui/stat"
import type { TestimonialItem } from "@/components/ui/testimonial"
export interface SaasLandingContent {
brand: ReactNode
nav: NavItem[]
navActions?: CtaItem[]
hero: {
eyebrow?: string
title: string
description: string
actions: CtaItem[]
note?: ReactNode
media?: ReactNode
}
logos?: { title?: string; items: LogoItem[] }
stats?: { title?: string; eyebrow?: string; items: StatItem[] }
features: {
eyebrow?: string
title: string
description?: string
items: FeatureItem[]
}
rows?: {
eyebrow?: string
title?: string
description?: string
items: FeatureRowItem[]
}
testimonials?: {
eyebrow?: string
title: string
description?: string
items: TestimonialItem[]
}
pricing?: {
eyebrow?: string
title: string
description?: string
items: PricingTierItem[]
note?: ReactNode
}
faq?: {
eyebrow?: string
title: string
description?: string
items: FaqItem[]
}
cta: {
eyebrow?: string
title: string
description?: string
actions: CtaItem[]
note?: ReactNode
}
footer: {
groups?: FooterGroup[]
brand?: ReactNode
copyright?: ReactNode
legal?: FooterLink[]
}
}
export interface SaasLandingProps {
content: SaasLandingContent
image?: ImageComponent
link?: LinkComponent
/** Marks the active nav item. */
currentPath?: string
/** Rendered above the header — a `Banner`, usually. */
banner?: ReactNode
}
export function SaasLanding({
content,
image,
link,
currentPath,
banner,
}: SaasLandingProps) {
const { hero, logos, stats, features, rows, testimonials, pricing, faq, cta, footer } =
content
return (
<>
{banner}
<NavTop
brand={content.brand}
items={content.nav}
actions={content.navActions}
link={link}
currentPath={currentPath}
/>
<main id="main">
{/* The only h1 on the page. */}
<HeroSplit
eyebrow={hero.eyebrow}
title={hero.title}
description={hero.description}
actions={hero.actions}
note={hero.note}
media={hero.media}
link={link}
headingLevel={1}
spacing="lg"
/>
{logos ? (
<LogoCloud
title={logos.title}
items={logos.items}
image={image}
link={link}
headingLevel={2}
spacing="sm"
/>
) : null}
<FeatureGrid
eyebrow={features.eyebrow}
title={features.title}
description={features.description}
items={features.items}
link={link}
headingLevel={2}
itemHeadingLevel={3}
align="center"
/>
{rows ? (
<FeatureRows
eyebrow={rows.eyebrow}
title={rows.title}
description={rows.description}
items={rows.items}
link={link}
headingLevel={2}
itemHeadingLevel={3}
align="center"
/>
) : null}
{stats ? (
<Stats
eyebrow={stats.eyebrow}
title={stats.title}
items={stats.items}
headingLevel={2}
className="border-y"
/>
) : null}
{testimonials ? (
<TestimonialsGrid
eyebrow={testimonials.eyebrow}
title={testimonials.title}
description={testimonials.description}
items={testimonials.items}
image={image}
headingLevel={2}
/>
) : null}
{pricing ? (
<PricingTiers
eyebrow={pricing.eyebrow}
title={pricing.title}
description={pricing.description}
items={pricing.items}
note={pricing.note}
link={link}
headingLevel={2}
tierHeadingLevel={3}
/>
) : null}
{faq ? (
<FaqAccordion
eyebrow={faq.eyebrow}
title={faq.title}
description={faq.description}
items={faq.items}
headingLevel={2}
itemHeadingLevel={3}
align="center"
schemaOrg
/>
) : null}
<CtaBand
eyebrow={cta.eyebrow}
title={cta.title}
description={cta.description}
actions={cta.actions}
note={cta.note}
link={link}
headingLevel={2}
variant="primary"
/>
</main>
<Footer
brand={footer.brand ?? content.brand}
groups={footer.groups}
copyright={footer.copyright}
legal={footer.legal}
link={link}
headingLevel={2}
/>
</>
)
}Props
SaasLandingProps
| Prop | Type | Default |
|---|---|---|
content*Required | SaasLandingContent | — |
image | ImageComponent | — |
link | LinkComponent | — |
currentPathMarks the active nav item. | string | — |
bannerRendered above the header — a `Banner`, usually. | ReactNode | — |