Hero proof
A centered hero with a proof row of numbers or logos under a hairline.
npx shadcn@latest add https://ui.goodtabs.co/r/hero-proof.json
With the @goodtabs namespace in components.json: npx shadcn@latest add @goodtabs/hero-proof
goodtabs UI
Production UI, picked by a person
Free components in one calm look, installed with one command.
By the numbers
- components
- 200
- installs
- 12k
- licence
- MIT
Centered hero with a caption and three numbers.
Logos
Ship the settings page by lunch
Forms, tables and dialogs that already follow your tokens.
Used by teams at
NorthwindHalcyonParcelLatticeFieldnote
Pass names or logos as children instead of numbers.
Four numbers
Open source
A component library you can read
Copy the source, change it, keep it. No runtime package to upgrade.
- stars
- 9.4k
- contributors
- 148
- components
- 212
- runtime deps
- 0
Four numbers, two by two when narrow.
Usage
import { HeroProof } from "@/components/hero-proof"
<HeroProof
title="Production UI, picked by a person"
description="Free components in one calm look."
proofLabel="By the numbers"
proof={[{ value: "200", label: "components" }, { value: "MIT", label: "licence" }]}
/>Props
| Prop | Type | Default | Description |
|---|---|---|---|
| eyebrow | string | Small uppercase line above the title. | |
| title | ReactNode | The headline. A string or your own element. | |
| description | ReactNode | One or two sentences under the title. | |
| actions | ReactNode | Buttons and links. | |
| proof | { value: string; label: string }[] | Three or four numbers for the proof row. | |
| proofLabel | string | Caption above the proof row. | |
| children | ReactNode | Logos or custom proof, shown when proof is not set. |
Source
hero-proof/hero-proof.tsx
import * as React from "react";
import { cn } from "@/lib/utils";
type Proof = { value: string; label: string };
type HeroProofProps = Omit<React.ComponentProps<"section">, "title"> & {
/** Small uppercase line above the title. */
eyebrow?: string;
/** The headline. Pass a string or your own element. */
title: React.ReactNode;
/** One or two sentences under the title. */
description?: React.ReactNode;
/** Buttons and links, usually one primary and one plain link. */
actions?: React.ReactNode;
/** Numbers for the proof row. Omit to pass logos as children instead. */
proof?: Proof[];
/** Caption above the proof row, for example "Trusted by teams at". */
proofLabel?: string;
/** Logos or any custom proof, shown in the proof row when proof is not set. */
children?: React.ReactNode;
};
/**
* A centered hero with a proof row under a hairline: numbers, or logos you pass as children.
* The row reflows by container width, so it works inside a card. Server component.
*/
export function HeroProof({
eyebrow,
title,
description,
actions,
proof,
proofLabel,
children,
className,
...props
}: HeroProofProps) {
const hasRow = (proof && proof.length > 0) || children;
return (
<section className={cn("@container w-full py-8 @2xl:py-16 @5xl:py-24", className)} {...props}>
<div className="mx-auto w-full max-w-6xl px-4 @2xl:px-6">
<div className="mx-auto flex max-w-3xl flex-col items-center gap-4 text-center @2xl:gap-5">
{eyebrow && <p className="m-0 font-mono text-xs tracking-wide text-muted-foreground uppercase">{eyebrow}</p>}
<h1 className="m-0 text-2xl leading-none font-medium tracking-tight text-balance @2xl:text-4xl @5xl:text-6xl">
{title}
</h1>
{description && (
<p className="m-0 max-w-xl text-sm text-balance text-muted-foreground @2xl:text-base @5xl:text-lg">
{description}
</p>
)}
{actions && <div className="flex flex-wrap items-center justify-center gap-2">{actions}</div>}
</div>
{hasRow && (
<div className="mt-8 border-t pt-6 @2xl:mt-14 @2xl:pt-8">
{proofLabel && (
<p className="m-0 mb-5 text-center font-mono text-xs tracking-wide text-muted-foreground uppercase">
{proofLabel}
</p>
)}
{proof && proof.length > 0 ? (
<dl
className={cn(
"m-0 grid gap-6 text-center",
proof.length >= 4 ? "grid-cols-2 @2xl:grid-cols-4" : "grid-cols-3",
)}
>
{proof.map((p) => (
<div key={p.label} className="flex flex-col-reverse gap-1">
<dt className="text-xs text-muted-foreground">{p.label}</dt>
<dd className="m-0 text-xl font-medium tracking-tight @2xl:text-3xl">{p.value}</dd>
</div>
))}
</dl>
) : (
<div className="flex flex-wrap items-center justify-center gap-x-8 gap-y-4 text-muted-foreground">
{children}
</div>
)}
</div>
)}
</div>
</section>
);
}
For AI agents
Hand your agent this page, the markdown at /components/hero-proof/prompt.md, or the Copy AI prompt button above.
When to use
- A landing hero where numbers or customer names make the case
- When you have three or four real figures, or five or fewer logos
- Inside a column or card, since it reflows by container width
When not to use
- When there is no proof yet; use the plain hero
- A product-first page, which wants the split hero
- With invented numbers or logos you do not have the right to show
Agent notes
- Reflows on container width (@2xl); numbers stay three across, four become two by two when narrow
- Pass either proof or children; proof wins when both are set
- Logos are slots: give them text-muted-foreground, they inherit the color
- Server component; the proof row is a dl, so numbers read as term and value