A readable policy page with a last-updated line, a sticky hairline table of contents that scrolls smoothly to each anchored section, prose sections with accent-bulleted lists, and a soft contact panel at the end.
Readable policy page: an eyebrow, a two-column heading, and a last-updated line above a sticky hairline table of contents that jumps to each anchored section, beside prose sections with accent-bulleted lists and a soft contact panel at the end.
Upgrade to Pro
Pro blocks install through the shadcn CLI with your license key and ship their full source. Docs and live previews stay open to everyone, so you can read every block's details first.
Swap YOUR_EMAIL and YOUR_KEY for the email and license key on your account. Find your license key on your account page.
Radix flavor
npx shadcn add "https://ui.beste.co/r/legal52?email=YOUR_EMAIL&license_key=YOUR_KEY"Base UI flavor
npx shadcn add "https://ui.beste.co/r-base/legal52?email=YOUR_EMAIL&license_key=YOUR_KEY"This installs the block to components/beste/block/legal52.tsx plus the badge23 and button21 components it uses for the eyebrow and the contact action.
The installed file exports legal52Demo alongside the block: the exact props behind the preview above. Spread it to get a working policy page in one line.
import { Legal52, legal52Demo } from "@/components/beste/block/legal52";
export default function PrivacyPage() {
return <Legal52 {...legal52Demo} />;
}Then replace the demo with your own props. Written out, a trimmed setup looks like this:
import { Legal52 } from "@/components/beste/block/legal52";
export default function PrivacyPage() {
return (
<Legal52
badge={{ label: "Legal" }}
heading="Privacy policy"
description="What we hold, why we hold it, and how you get it back."
updated="Last updated April 2, 2026"
tocLabel="On this page"
sections={[
{
slug: "what-we-collect",
title: "What we collect",
paragraphs: ["We collect the account details your team enters and technical logs."],
bullets: ["Account data: names, work emails, roles", "Technical data: device and IP"],
},
{
slug: "your-rights",
title: "Your rights",
paragraphs: ["You can export or delete a workspace at any time, in an open format."],
},
]}
contact={{
title: "Questions about any of this?",
description: "Our data protection contact answers policy questions directly.",
button: { label: "Contact the privacy team", href: "/contact" },
}}
/>
);
}| Prop | Type | Default | Description |
|---|---|---|---|
badge | { label: string } | – | Monospace eyebrow above the hairline rule, rendered via Badge23 |
heading | string | – | Document title, rendered as the h1 |
description | string | – | Plain-language summary, right-aligned from md up |
updated | string | – | Last-updated line under the heading row |
tocLabel | string | – | Label above the table of contents |
sections | Section[] | [] | Document sections, each anchored by its slug |
contact | Contact | – | Soft panel at the end of the document |
className | string | – | Extra classes for the outer <section> |
type ActionLink = { label: string; href: string };
type Section = {
slug: string;
title: string;
paragraphs: string[];
bullets?: string[];
};
type Contact = {
title: string;
description: string;
button: ActionLink;
};sections, so it can never drift from the document: every entry links to #{slug} and the matching section carries that slug as its id.slug values have to be unique on the page. Two instances of this block on one route would produce duplicate ids and the first match would win.scroll-mt-24, so a jump from the contents leaves the heading clear of a sticky site header instead of tucking it underneath.lg (lg:sticky lg:top-24) and sits above the prose as a plain list below that width, so on a phone it reads as a summary rather than a floating rail.h2 under the document's h1, giving a policy page a correct outline without any extra configuration.max-w-2xl inside a wider column, which holds the measure near a comfortable line length while the contents rail keeps its own width.legal43
Full privacy policy content card with sections covering data collection, usage, sharing, retention, rights, and security. Ideal for dedicated privacy pages.
legal2
Shows privacy policy content with expandable accordion sections for each topic. Perfect for organizing lengthy privacy policies into scannable segments.
legal50
Multi-section policy card using tabs to switch between data use, security, cookies, and user rights content. Compact way to present multiple policies.
legal55
A legal section that sets each binding clause against a plain-language reading of it on hairline rows, keyed by clause references, with a precedence disclaimer under the last row.