# DesignRevision — Full Component Source > Every published component's full source, install command, and examples, concatenated for single-request ingestion. Index: https://designrevision.com/llms.txt --- # Shadcn Accordion > Free shadcn/ui accordion component for React — Radix-powered collapsible sections. The FAQ accordion, multiple-open, leading icons, contained cards, a plus/minus toggle, and controlled state. The examples official shadcn never shipped. Source: https://designrevision.com/components/accordion The **Shadcn Accordion** is the collapsible-sections component — the canonical shadcn/ui accordion built on Radix. Its number-one job is the **FAQ**, and the examples below cover that plus multiple-open, icons, a contained-card style, a plus/minus toggle, and controlled state. ## Built on Radix Accordion is a thin wrapper over Radix Accordion with four parts — `Accordion`, `AccordionItem`, `AccordionTrigger`, `AccordionContent`. Radix handles the WAI-ARIA accordion pattern: keyboard navigation, focus, and the `--radix-accordion-content-height` measurement that drives the open/close animation. The chevron rotates on open via `[&[data-state=open]>svg]:rotate-180`. ## The FAQ accordion The most common accordion is the **FAQ** — questions as triggers, answers as content, `type="single" collapsible` so one opens at a time and can close again: ```tsx What is your refund policy? 30-day money-back guarantee… ``` For search, pair it with **FAQPage structured data** (JSON-LD) so the Q&A can surface as rich results — this gallery emits that schema on every component page. The FAQ example is the markup. ## Single vs multiple `type="single"` opens one item at a time (add `collapsible` to allow closing it); `type="multiple"` lets several stay open and makes `value` an **array**. Use single for FAQs where focus matters, multiple for reference content the user scans across (the Multiple open example). ## Styling The default is flush rows with a bottom border. Restyle with classNames — no custom component: - **Leading icons** — put an icon before the label inside `AccordionTrigger` (the Leading icons example). - **Contained cards** — give `AccordionItem` `rounded-lg border px-4` and the `Accordion` `space-y-2` for separated cards (the Contained / card example). - **Plus / minus** — compose `AccordionPrimitive.Trigger` directly and toggle a `PlusIcon` / `MinusIcon` with `group-data-[state]` (the Plus / minus example). ## Controlled & disabled Use `defaultValue` for an uncontrolled start, or `value` + `onValueChange` to **control** which item is open from elsewhere (a stepper, a URL). Disable an item with the `disabled` prop on `AccordionItem`. The Controlled & disabled example drives the accordion from external buttons and disables one step. ## Animation The expand/collapse uses the `accordion-down` and `accordion-up` keyframes, which animate to Radix's `--radix-accordion-content-height`. **These aren't core Tailwind** — they come from `tw-animate-css` (or `tailwindcss-animate`). Add `@import "tw-animate-css";` to your global CSS, or the panel snaps open with no transition. ## Tabs vs Accordion Use an Accordion for **stacked, vertical** sections where several may be open — FAQs, settings, long forms — especially on narrow screens. Use [Tabs](/components/tabs) for **peer** sections shown one at a time in a fixed area. Accordions expand downward; tabs switch in place. ## Accessibility & theming Radix gives the accordion full keyboard support — Enter/Space toggles, arrow keys move between triggers, Home/End jump to the ends — and wires the trigger to its panel with `aria-controls`. It reads your design tokens — `--border`, `--ring` (focus), `--muted-foreground` (the chevron) — so it follows your theme, including dark mode, with no overrides. ## Installation ### accordion **Install:** ```bash npx shadcn@latest add @designrevision/accordion ``` **Dependencies:** @radix-ui/react-accordion, utils **Props** | Prop | Type | Default | Description | | --- | --- | --- | --- | | type * | "single" \| "multiple" | — | On Accordion. "single" opens one item at a time; "multiple" allows several. | | collapsible | boolean | false | On Accordion (type="single") — lets the open item close again. | | value / defaultValue | string \| string[] | — | The open item(s) — controlled (value + onValueChange) or uncontrolled (defaultValue). | | disabled | boolean | false | On AccordionItem — prevents it from opening. | `*` required. **Usage & accessibility:** A Radix accordion — four parts (Accordion, AccordionItem, AccordionTrigger, AccordionContent), with a chevron that rotates on open. Set type="single" collapsible for an FAQ, or type="multiple" to allow several open. The open/close animation uses the accordion-down / accordion-up keyframes from tw-animate-css (or tailwindcss-animate) — without it the panel snaps open. ```tsx // components/ui/accordion.tsx "use client" import * as React from "react" import * as AccordionPrimitive from "@radix-ui/react-accordion" import { ChevronDownIcon } from "lucide-react" import { cn } from "@/lib/utils" function Accordion({ ...props }: React.ComponentProps) { return } function AccordionItem({ className, ...props }: React.ComponentProps) { return ( ) } function AccordionTrigger({ className, children, ...props }: React.ComponentProps) { return ( svg]:rotate-180", className )} {...props} > {children} ) } function AccordionContent({ className, children, ...props }: React.ComponentProps) { return (
{children}
) } export { Accordion, AccordionItem, AccordionTrigger, AccordionContent } ``` ## Examples ### Accordion The canonical single-open, collapsible accordion — one item expanded at a time. **Install:** ```bash npx shadcn@latest add @designrevision/accordion-demo-01 ``` **Dependencies:** @designrevision/accordion ```tsx // components/ui/accordion-demo-01.tsx import { Accordion, AccordionContent, AccordionItem, AccordionTrigger, } from "@/components/ui/accordion" export default function AccordionDemo01() { return ( Is it accessible? Yes. It adheres to the WAI-ARIA design pattern and is fully keyboard navigable. Is it styled? Yes. It comes with default styles that match your design tokens, and you can override them with a className. Is it animated? Yes. It animates open and closed with the accordion-down / accordion-up keyframes. ) } ``` --- ### FAQ A frequently-asked-questions accordion — the most common use, and a natural pair for **FAQPage** schema. **Install:** ```bash npx shadcn@latest add @designrevision/accordion-faq-01 ``` **Dependencies:** @designrevision/accordion ```tsx // components/ui/accordion-faq-01.tsx import { Accordion, AccordionContent, AccordionItem, AccordionTrigger, } from "@/components/ui/accordion" const faqs = [ { q: "What is your refund policy?", a: "We offer a 30-day money-back guarantee — no questions asked. Email support and we'll process it within 48 hours.", }, { q: "Can I change my plan later?", a: "Yes. Upgrade or downgrade at any time from your billing settings; changes are prorated automatically.", }, { q: "Do you offer a free trial?", a: "Every paid plan includes a 14-day free trial. No credit card required to start.", }, { q: "How do I cancel my subscription?", a: "Cancel anytime from Settings → Billing. Your plan stays active until the end of the current period.", }, ] export default function AccordionFaq01() { return ( {faqs.map((faq, index) => ( {faq.q} {faq.a} ))} ) } ``` --- ### Multiple open `type="multiple"` lets several sections stay open at once, with two open by default. **Install:** ```bash npx shadcn@latest add @designrevision/accordion-multiple-01 ``` **Dependencies:** @designrevision/accordion ```tsx // components/ui/accordion-multiple-01.tsx import { Accordion, AccordionContent, AccordionItem, AccordionTrigger, } from "@/components/ui/accordion" const sections = [ { value: "shipping", title: "Shipping", body: "Free standard shipping on orders over $50. Express options at checkout." }, { value: "returns", title: "Returns", body: "Return unworn items within 30 days for a full refund." }, { value: "warranty", title: "Warranty", body: "All products carry a two-year limited warranty against defects." }, ] export default function AccordionMultiple01() { return ( {sections.map((section) => ( {section.title} {section.body} ))} ) } ``` --- ### Leading icons An icon before each trigger label — the help-center category pattern. **Install:** ```bash npx shadcn@latest add @designrevision/accordion-icons-01 ``` **Dependencies:** lucide-react, @designrevision/accordion ```tsx // components/ui/accordion-icons-01.tsx import { CreditCardIcon, ShieldCheckIcon, TruckIcon } from "lucide-react" import { Accordion, AccordionContent, AccordionItem, AccordionTrigger, } from "@/components/ui/accordion" const items = [ { value: "shipping", icon: TruckIcon, title: "Shipping & delivery", body: "Orders ship within 1–2 business days with tracked delivery." }, { value: "payment", icon: CreditCardIcon, title: "Payment methods", body: "We accept all major cards, Apple Pay, and PayPal." }, { value: "security", icon: ShieldCheckIcon, title: "Security", body: "Every transaction is protected with 256-bit encryption." }, ] export default function AccordionIcons01() { return ( {items.map((item) => ( {item.title} {item.body} ))} ) } ``` --- ### Contained / card Each item wrapped as a bordered card with spacing between, instead of the default flush rows. **Install:** ```bash npx shadcn@latest add @designrevision/accordion-card-01 ``` **Dependencies:** @designrevision/accordion ```tsx // components/ui/accordion-card-01.tsx import { Accordion, AccordionContent, AccordionItem, AccordionTrigger, } from "@/components/ui/accordion" const items = [ { value: "a", title: "Getting started", body: "Install the CLI and run the init command to scaffold your project." }, { value: "b", title: "Configuration", body: "Edit the config file to set your theme tokens and import aliases." }, { value: "c", title: "Deployment", body: "Push to your provider and connect the repo — builds run automatically." }, ] export default function AccordionCard01() { return ( {items.map((item) => ( {item.title} {item.body} ))} ) } ``` --- ### Plus / minus icon A custom trigger that swaps the default chevron for a plus/minus that toggles via `group-data-[state]`. **Install:** ```bash npx shadcn@latest add @designrevision/accordion-plus-01 ``` **Dependencies:** @radix-ui/react-accordion, lucide-react, @designrevision/accordion ```tsx // components/ui/accordion-plus-01.tsx import * as AccordionPrimitive from "@radix-ui/react-accordion" import { MinusIcon, PlusIcon } from "lucide-react" import { Accordion, AccordionContent, AccordionItem, } from "@/components/ui/accordion" const items = [ { value: "a", title: "Can I use this in a commercial project?", body: "Yes — it's free to use in personal and commercial projects." }, { value: "b", title: "Do I need to credit you?", body: "No attribution is required, though it's always appreciated." }, { value: "c", title: "Will there be updates?", body: "Yes. New components and examples ship regularly." }, ] export default function AccordionPlus01() { return ( {items.map((item) => ( {/* Custom trigger: swap the default chevron for a +/- toggle. */} {item.title} {item.body} ))} ) } ``` --- ### Controlled & disabled Drive the open item from external buttons via `value` / `onValueChange`, with one disabled step. **Install:** ```bash npx shadcn@latest add @designrevision/accordion-controlled-01 ``` **Dependencies:** @designrevision/accordion, button ```tsx // components/ui/accordion-controlled-01.tsx "use client" import * as React from "react" import { Accordion, AccordionContent, AccordionItem, AccordionTrigger, } from "@/components/ui/accordion" import { Button } from "@/components/ui/button" const steps = [ { value: "account", title: "Account", body: "Create your account and verify your email address." }, { value: "profile", title: "Profile", body: "Add your name and a profile photo." }, { value: "billing", title: "Billing", body: "Add a payment method — available once your profile is complete.", disabled: true }, ] export default function AccordionControlled01() { const [value, setValue] = React.useState("account") return (
{steps.map((step) => ( ))}
{steps.map((step) => ( {step.title} {step.body} ))}
) } ``` --- # Shadcn Alert > Free shadcn/ui alert component for React — an inline message banner with an icon, title, and description. Default and destructive variants plus success/warning/info, actions, dismissible, and a full-width banner. Source: https://designrevision.com/components/alert The **Shadcn Alert** is an **inline message banner** — a bordered box with an optional icon, a title, and a description that surfaces a status or notice in the page flow. It ships **default** and **destructive** variants, lays out its icon automatically, and restyles easily for success, warning, and info. The examples below cover variants, icons, actions, a dismissible alert, and a full-width banner. ## Alert vs Alert Dialog These are two different components — don't mix them up: - **Alert** (this page) — an **inline** message that lives in the layout. A banner, a form-error summary, a notice. - **[Alert Dialog](/components/alert-dialog)** — a **modal** that interrupts the user to confirm or cancel a destructive action. If you want a message *in* the page, use Alert. If you need a blocking confirm, use Alert Dialog. ## Anatomy Three parts, composed: ```tsx Heads up! You can add components to your app using the CLI. ``` Drop a Lucide icon as the **first child** and the component's CSS grid detects it (`has-[>svg]`) and creates an icon column automatically. With no icon, that column collapses and the content spans the full width. ## Variants Two are built in: ```tsx Error Your session has expired. ``` For **success / warning / info**, override the border, text, and icon color with a `className` (the Success / warning / info example shows green, amber, and blue). If you use them often, add them to the `cva` variants so `variant="success"` works directly. ## Actions and dismissing - **Actions** — add buttons under the content (aligned to the text column with `col-start-2`) for "Update now / Later" style notices. - **Dismissible** — track an open state and render a corner close button (the Dismissible example uses `useState`). For transient, stacking notifications instead, reach for a **[toast](/components/sonner)** (Sonner). ## Announcement banner For a high-emphasis, full-width bar, give the Alert a colored background and an inline link — the Announcement banner example uses `bg-primary text-primary-foreground` for a promo or notice at the top of a page. ## Accessibility The Alert sets `role="alert"`, so assistive tech announces its content as it appears — right for important, time-sensitive messages. For routine notices you can soften the urgency (e.g. `role="status"`). Keep the meaning in the title and description; decorative icons are not announced. ## Installation ### alert **Install:** ```bash npx shadcn@latest add @designrevision/alert ``` **Dependencies:** utils **Props** | Prop | Type | Default | Description | | --- | --- | --- | --- | | variant | "default" \| "destructive" | "default" | Visual style. default for neutral messages, destructive for errors. | **Usage & accessibility:** An inline alert (a message banner) — distinct from Alert Dialog (the modal). Compose with and ; drop a Lucide icon as the first child and the CSS grid lays it out automatically. variant is "default" or "destructive" (extend the cva for success/warning/info). It carries role="alert" so screen readers announce it. ```tsx // components/ui/alert.tsx import * as React from "react" import { cva, type VariantProps } from "class-variance-authority" import { cn } from "@/lib/utils" const alertVariants = cva( "relative w-full rounded-lg border px-4 py-3 text-sm grid has-[>svg]:grid-cols-[calc(var(--spacing)*4)_1fr] grid-cols-[0_1fr] has-[>svg]:gap-x-3 gap-y-0.5 items-start [&>svg]:size-4 [&>svg]:translate-y-0.5 [&>svg]:text-current", { variants: { variant: { default: "bg-card text-card-foreground", destructive: "text-destructive bg-card [&>svg]:text-current *:data-[slot=alert-description]:text-destructive/90", }, }, defaultVariants: { variant: "default", }, } ) function Alert({ className, variant, ...props }: React.ComponentProps<"div"> & VariantProps) { return (
) } function AlertTitle({ className, ...props }: React.ComponentProps<"div">) { return (
) } function AlertDescription({ className, ...props }: React.ComponentProps<"div">) { return (
) } export { Alert, AlertTitle, AlertDescription } ``` ## Examples ### Default & destructive The canonical alert — icon, title, and description in the two built-in variants. **Install:** ```bash npx shadcn@latest add @designrevision/alert-demo-01 ``` **Dependencies:** lucide-react, alert ```tsx // components/ui/alert-demo-01.tsx import { CircleAlertIcon, TerminalIcon } from "lucide-react" import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert" export default function AlertDemo01() { return (
Heads up! You can add components to your app using the CLI. Error Your session has expired. Please log in again.
) } ``` --- ### Success / warning / info Extra status colors built by overriding the border, text, and icon color. **Install:** ```bash npx shadcn@latest add @designrevision/alert-variants-01 ``` **Dependencies:** lucide-react, alert ```tsx // components/ui/alert-variants-01.tsx import { CircleCheckIcon, InfoIcon, TriangleAlertIcon } from "lucide-react" import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert" export default function AlertVariants01() { return (
Success Your changes have been saved. Warning Your trial ends in 3 days. Information A new version is available.
) } ``` --- ### Title-only & description-only Minimal alerts — the icon column collapses automatically when there's no icon. **Install:** ```bash npx shadcn@latest add @designrevision/alert-simple-01 ``` **Dependencies:** lucide-react, alert ```tsx // components/ui/alert-simple-01.tsx import { CheckIcon } from "lucide-react" import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert" export default function AlertSimple01() { return (
{/* Title only, with an icon */} Your changes have been saved. {/* Description only, no icon — the grid collapses the icon column */} We use cookies to improve your experience.
) } ``` --- ### With actions Trailing action buttons aligned under the content — the update/notice pattern. **Install:** ```bash npx shadcn@latest add @designrevision/alert-action-01 ``` **Dependencies:** lucide-react, alert, button ```tsx // components/ui/alert-action-01.tsx import { RocketIcon } from "lucide-react" import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert" import { Button } from "@/components/ui/button" export default function AlertAction01() { return ( Update available A new version is ready to install.
) } ``` --- ### Dismissible A closable alert — an X button toggles it with `useState`. **Install:** ```bash npx shadcn@latest add @designrevision/alert-dismissible-01 ``` **Dependencies:** lucide-react, alert, button ```tsx // components/ui/alert-dismissible-01.tsx "use client" import * as React from "react" import { InfoIcon, XIcon } from "lucide-react" import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert" import { Button } from "@/components/ui/button" export default function AlertDismissible01() { const [open, setOpen] = React.useState(true) if (!open) { return ( ) } return ( New feature Dark mode is now available in settings. ) } ``` --- ### Announcement banner A full-width, high-emphasis banner — a colored Alert with an inline link. **Install:** ```bash npx shadcn@latest add @designrevision/alert-banner-01 ``` **Dependencies:** lucide-react, alert ```tsx // components/ui/alert-banner-01.tsx import { MegaphoneIcon } from "lucide-react" import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert" export default function AlertBanner01() { return ( Black Friday — 50% off all plans Offer ends Monday.{" "} Claim it now . ) } ``` --- ### With a list A destructive alert whose description holds a bulleted list — a form-error summary. **Install:** ```bash npx shadcn@latest add @designrevision/alert-list-01 ``` **Dependencies:** lucide-react, alert ```tsx // components/ui/alert-list-01.tsx import { CircleAlertIcon } from "lucide-react" import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert" export default function AlertList01() { return ( Unable to submit your form
  • Email is required
  • Password must be at least 8 characters
  • You must accept the terms
) } ``` --- # Shadcn Alert Dialog > Free shadcn/ui alert dialog component for React — destructive confirmations, async actions with a spinner, type-to-confirm, and controlled open. The examples official shadcn never shipped. Source: https://designrevision.com/components/alert-dialog The **Shadcn Alert Dialog** is the interrupting confirmation — the canonical shadcn/ui alert dialog built on Radix, with `AlertDialogAction` and `AlertDialogCancel` pre-styled from your `buttonVariants`. Official ships it with **zero examples**; the ones below cover what you actually need — destructive deletes, async actions, and type-to-confirm guards. ## When to use — Alert Dialog vs Dialog Use an **Alert Dialog** when an action needs a deliberate yes/no and shouldn't be dismissed by accident — **delete, discard, sign out, revoke**. It has **no close X**, doesn't close on an outside click, and is announced as an alert. Use a [Dialog](/components/dialog) for general modal content (forms, details) — it has a close X and dismisses on outside-click and Escape. If you find yourself wanting an alert dialog to close on outside click, you want a Dialog. ## Destructive actions The most common alert dialog is a destructive confirmation. The action button defaults to your primary color; make it **red** by passing the destructive variant: `className={buttonVariants({ variant: "destructive" })}` on `AlertDialogAction`. Keep the title a plain question ("Delete this project?") and the description specific about consequences. ## Async actions By default the action **closes** the dialog the instant it's clicked — wrong when the work is async. Control the `open` state, call `event.preventDefault()` in the action's `onClick`, run the request, and `setOpen(false)` only on success — disabling both buttons and showing a spinner in between (and blocking Escape while in flight). The Async action example is the complete pattern. ## Type-to-confirm For irreversible, high-blast-radius actions, gate the action behind a verification phrase: keep `AlertDialogAction` `disabled` until the user types the exact value (e.g. `DELETE`). It's the GitHub "type the repo name" pattern, and it's a few lines — see the Type-to-confirm example. ## Triggering & the dropdown gotcha Open it declaratively with `AlertDialogTrigger`, or **controlled** from state (no trigger) to confirm an action started elsewhere. A frequent bug — "the alert dialog **closes immediately**" — happens when the trigger is nested inside a `DropdownMenuItem`: the menu closing steals focus and dismisses the dialog. The fix is to control the alert dialog's `open` and set it from the menu item's `onSelect`, rendering the dialog as a sibling rather than nesting the trigger. ## Accessibility Radix gives the alert dialog `role="alertdialog"`, traps focus, moves initial focus to the **Cancel** button (the safe default), and returns focus to the trigger on close. Always include an `AlertDialogTitle` and `AlertDialogDescription` so the prompt is announced. Escape closes it; outside-click does not. ## Theming The alert dialog reads your design tokens (`--background`, `--border`, `--destructive`) and the shared `buttonVariants`, so it follows your theme — including dark mode — with zero overrides. ## Installation ### alert-dialog **Install:** ```bash npx shadcn@latest add @designrevision/alert-dialog ``` **Dependencies:** @radix-ui/react-alert-dialog, utils, button **Props** | Prop | Type | Default | Description | | --- | --- | --- | --- | | open | boolean | — | Controlled open state (on AlertDialog). Pair with onOpenChange. | | onOpenChange | (open: boolean) => void | — | Fires when the dialog opens or closes (on AlertDialog). | **Usage & accessibility:** Eleven parts; AlertDialogAction and AlertDialogCancel are pre-styled with buttonVariants() (primary) and buttonVariants({variant:'outline'}). No close X. Closes on Escape but NOT on outside-click by design. Make the action destructive with className={buttonVariants({variant:'destructive'})}. ```tsx // components/ui/alert-dialog.tsx "use client" import * as React from "react" import * as AlertDialogPrimitive from "@radix-ui/react-alert-dialog" import { cn } from "@/lib/utils" import { buttonVariants } from "@/components/ui/button" function AlertDialog({ ...props }: React.ComponentProps) { return } function AlertDialogTrigger({ ...props }: React.ComponentProps) { return ( ) } function AlertDialogPortal({ ...props }: React.ComponentProps) { return ( ) } function AlertDialogOverlay({ className, ...props }: React.ComponentProps) { return ( ) } function AlertDialogContent({ className, ...props }: React.ComponentProps) { return ( ) } function AlertDialogHeader({ className, ...props }: React.ComponentProps<"div">) { return (
) } function AlertDialogFooter({ className, ...props }: React.ComponentProps<"div">) { return (
) } function AlertDialogTitle({ className, ...props }: React.ComponentProps) { return ( ) } function AlertDialogDescription({ className, ...props }: React.ComponentProps) { return ( ) } function AlertDialogAction({ className, ...props }: React.ComponentProps) { return ( ) } function AlertDialogCancel({ className, ...props }: React.ComponentProps) { return ( ) } export { AlertDialog, AlertDialogPortal, AlertDialogOverlay, AlertDialogTrigger, AlertDialogContent, AlertDialogHeader, AlertDialogFooter, AlertDialogTitle, AlertDialogDescription, AlertDialogAction, AlertDialogCancel, } ``` ## Examples ### Basic A confirmation with a `AlertDialogCancel` (outline) and `AlertDialogAction` (primary) — the canonical interruptive prompt. **Install:** ```bash npx shadcn@latest add @designrevision/alert-dialog-demo-01 ``` **Dependencies:** alert-dialog, button ```tsx // components/ui/alert-dialog-demo-01.tsx import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, AlertDialogTrigger, } from "@/components/ui/alert-dialog" import { Button } from "@/components/ui/button" export default function AlertDialogDemo01() { return ( Are you absolutely sure? This action cannot be undone. This will permanently delete your account and remove your data from our servers. Cancel Continue ) } ``` --- ### Destructive A delete confirmation with a **red** action — `className={buttonVariants({ variant: "destructive" })}` on `AlertDialogAction`. **Install:** ```bash npx shadcn@latest add @designrevision/alert-dialog-destructive-01 ``` **Dependencies:** alert-dialog, button ```tsx // components/ui/alert-dialog-destructive-01.tsx import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, AlertDialogTrigger, } from "@/components/ui/alert-dialog" import { Button, buttonVariants } from "@/components/ui/button" export default function AlertDialogDestructive01() { return ( Delete this project? This permanently deletes the project and all of its data. This action cannot be undone. Cancel Delete ) } ``` --- ### Async action Keeps the dialog **open** while the action runs — `preventDefault()` on the action, a `Loader2` spinner, disabled buttons, then close on success. **Install:** ```bash npx shadcn@latest add @designrevision/alert-dialog-async-01 ``` **Dependencies:** lucide-react, alert-dialog, button ```tsx // components/ui/alert-dialog-async-01.tsx "use client" import * as React from "react" import { Loader2Icon } from "lucide-react" import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, AlertDialogTrigger, } from "@/components/ui/alert-dialog" import { Button, buttonVariants } from "@/components/ui/button" export default function AlertDialogAsync01() { const [open, setOpen] = React.useState(false) const [loading, setLoading] = React.useState(false) const onConfirm = async () => { setLoading(true) await new Promise((resolve) => setTimeout(resolve, 1500)) setLoading(false) setOpen(false) } return ( !loading && setOpen(next)}> Delete account? This permanently deletes your account. The dialog stays open while the request is in flight. Cancel { event.preventDefault() onConfirm() }} className={buttonVariants({ variant: "destructive" })} > {loading ? ( <> Deleting… ) : ( "Delete" )} ) } ``` --- ### Type to confirm A GitHub-style guard — the destructive action stays disabled until the user types `DELETE`. **Install:** ```bash npx shadcn@latest add @designrevision/alert-dialog-confirm-input-01 ``` **Dependencies:** alert-dialog, button, input, label ```tsx // components/ui/alert-dialog-confirm-input-01.tsx "use client" import * as React from "react" import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, AlertDialogTrigger, } from "@/components/ui/alert-dialog" import { Button, buttonVariants } from "@/components/ui/button" import { Input } from "@/components/ui/input" import { Label } from "@/components/ui/label" export default function AlertDialogConfirmInput01() { const [value, setValue] = React.useState("") const confirmed = value === "DELETE" return ( setValue("")}> Delete repository This cannot be undone. Type{" "} DELETE to confirm.
setValue(event.target.value)} placeholder="DELETE" autoComplete="off" />
Cancel Delete
) } ``` --- ### Controlled Opened from React state with no `AlertDialogTrigger` — for confirming an action initiated elsewhere. **Install:** ```bash npx shadcn@latest add @designrevision/alert-dialog-controlled-01 ``` **Dependencies:** alert-dialog, button ```tsx // components/ui/alert-dialog-controlled-01.tsx "use client" import * as React from "react" import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, } from "@/components/ui/alert-dialog" import { Button } from "@/components/ui/button" export default function AlertDialogControlled01() { const [open, setOpen] = React.useState(false) return ( <> Discard changes? You have unsaved changes. Leaving now will discard them. This dialog is opened from state, with no AlertDialogTrigger. Keep editing Discard ) } ``` --- ### With icon A centered warning-icon header for the highest-stakes confirmations. **Install:** ```bash npx shadcn@latest add @designrevision/alert-dialog-icon-01 ``` **Dependencies:** lucide-react, alert-dialog, button ```tsx // components/ui/alert-dialog-icon-01.tsx import { TriangleAlertIcon } from "lucide-react" import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, AlertDialogTrigger, } from "@/components/ui/alert-dialog" import { Button, buttonVariants } from "@/components/ui/button" export default function AlertDialogIcon01() { return (
Revoke access? This removes the member's access immediately. They'll need a new invite to return.
Cancel Revoke
) } ``` --- # Shadcn Area Chart > Free shadcn/ui area chart for React — built on Recharts and themed with CSS variables. Stacked areas, gradient fills, legends, an interactive time-range variant, plus loading, empty, and accessible states. Source: https://designrevision.com/components/area-chart The **Shadcn Area Chart** is a Recharts `AreaChart` wrapped in the shadcn `ChartContainer` — themed with your `--chart` CSS variables and dark-mode-ready. It's a line chart with the area below filled, ideal for **volume and cumulative trends**. Below: stacked areas, gradient fills, legends, an interactive time-range variant, and the production states. ## The basic area chart ```tsx const chartConfig = { desktop: { label: "Desktop", color: "var(--chart-1)" }, } satisfies ChartConfig } /> ``` ## Stacking Give areas a shared `stackId` and they sum into a cumulative band: ```tsx ``` For a proportional (100%) chart, set `stackOffset="expand"` on the `AreaChart`. ## Gradient fills Define a `linearGradient` in `` and point the fill at it — the signature area look: ```tsx ``` ## Interactive time range The Interactive example holds a range in state, filters the data by date, and drives it with a ` Last 30 days Last 14 days Last 7 days new Date(value).toLocaleDateString("en-US", { month: "short", day: "numeric", }) } /> new Date(value).toLocaleDateString("en-US", { month: "short", day: "numeric", }) } indicator="dot" /> } /> } /> ) } ``` --- ### Loading state A skeleton shaped like the area chart while data loads. **Install:** ```bash npx shadcn@latest add @designrevision/area-chart-loading-01 ``` **Dependencies:** card, skeleton ```tsx // components/ui/area-chart-loading-01.tsx import { Card, CardContent, CardHeader } from "@/components/ui/card" import { Skeleton } from "@/components/ui/skeleton" // A loading state for an area chart — header placeholders + a chart-area skeleton. export default function AreaChartLoading01() { return (
{Array.from({ length: 6 }).map((_, i) => ( ))}
) } ``` --- ### Empty state A graceful no-data state with an icon and message. **Install:** ```bash npx shadcn@latest add @designrevision/area-chart-empty-01 ``` **Dependencies:** lucide-react, card ```tsx // components/ui/area-chart-empty-01.tsx import { ChartArea } from "lucide-react" import { Card, CardContent, CardDescription, CardHeader, CardTitle, } from "@/components/ui/card" export default function AreaChartEmpty01() { return ( Area Chart January - June 2024
No data to display

Once events start flowing in, your trend will show here.

) } ``` --- ### Accessible Keyboard-navigable with a screen-reader data-table fallback. **Install:** ```bash npx shadcn@latest add @designrevision/area-chart-a11y-01 ``` **Dependencies:** recharts, chart, card ```tsx // components/ui/area-chart-a11y-01.tsx "use client" import { Area, AreaChart, CartesianGrid, XAxis } from "recharts" import { Card, CardContent, CardDescription, CardHeader, CardTitle, } from "@/components/ui/card" import { type ChartConfig, ChartContainer, ChartTooltip, ChartTooltipContent, } from "@/components/ui/chart" const chartData = [ { month: "January", desktop: 186 }, { month: "February", desktop: 305 }, { month: "March", desktop: 237 }, { month: "April", desktop: 73 }, { month: "May", desktop: 209 }, { month: "June", desktop: 214 }, ] const chartConfig = { desktop: { label: "Desktop", color: "var(--chart-1)" }, } satisfies ChartConfig export default function AreaChartA11y01() { return ( Accessible Area Chart Keyboard-navigable, with a screen-reader data table value.slice(0, 3)} /> } /> {/* Visually-hidden data-table fallback for assistive technology. */} {chartData.map((row) => ( ))}
Desktop visitors by month, January to June 2024
Month Visitors
{row.month} {row.desktop}
) } ``` --- # Shadcn Avatar > Free shadcn/ui avatar component for React — a Radix image with a graceful initials fallback. Sizes, an avatar group/stack, status indicators, and square/ringed shapes. The examples official shadcn never shipped. Source: https://designrevision.com/components/avatar The **Shadcn Avatar** is the user image — the canonical shadcn/ui avatar built on Radix, with a **graceful fallback** to initials when the image is missing or still loading. Official ships one demo; the examples below cover the real uses — **groups, status dots, sizes, and shapes**. ## Built on Radix Avatar wraps Radix Avatar with three parts: ```tsx CN ``` `AvatarImage` renders **only once the image has loaded**; until then — or if it fails — `AvatarFallback` shows. That's the whole point: avatars never break into a busted-image icon. Put initials in the fallback. ## Fallbacks & initials For users without a photo, skip the image (or let it fail) and the `AvatarFallback` initials fill the circle. Give the fallback a background and text color for **colored initials** (`bg-rose-500 text-white`) — a common pattern for distinguishing people in a list. The Fallbacks example shows plain and colored. ## Sizes & shapes Size with a className on `Avatar` — the default is `size-8`; use `size-6` … `size-16` (the Sizes example). For a **square** avatar, set `rounded-lg` on the `Avatar` and `AvatarFallback` to override the default `rounded-full`; add `ring-2 ring-offset-2` for a ringed look (the Shape & ring example). ## Avatar groups A stacked **avatar group** is a flex row with a negative gap so they overlap, plus a ring on each so they separate cleanly: ```tsx
+3
``` The Avatar group example includes the "+N" overflow. ## Status & context Pin a **status dot** to the avatar by wrapping it in a `relative` container and absolutely positioning a small ringed `rounded-full` span at the bottom-right — green for online, amber for away, muted for offline (the Status indicator example). In a list or menu, set the avatar beside a name and email (the With name example) — the pattern this gallery now uses in its [Card](/components/card), [Dropdown Menu](/components/dropdown-menu), and [Popover](/components/popover) user examples. ## Accessibility & theming Give `AvatarImage` a meaningful `alt` (the person's name), or `alt=""` when the name is already shown beside it. Don't rely on the **status dot color alone** — pair it with a label. The avatar reads your design tokens — `--muted` (the fallback background), `--background` (group rings) — so it follows your theme, including dark mode, with no overrides. ## Installation ### avatar **Install:** ```bash npx shadcn@latest add @designrevision/avatar ``` **Dependencies:** @radix-ui/react-avatar, utils **Usage & accessibility:** A Radix avatar — three parts (Avatar, AvatarImage, AvatarFallback). AvatarImage renders only once the image has loaded; until then (or if it fails) AvatarFallback shows — put initials there. Size it with a className on Avatar (default size-8); use rounded-lg for a square avatar, and stack a group with -space-x-* plus a ring. Always give AvatarImage an alt. ```tsx // components/ui/avatar.tsx "use client" import * as React from "react" import * as AvatarPrimitive from "@radix-ui/react-avatar" import { cn } from "@/lib/utils" function Avatar({ className, ...props }: React.ComponentProps) { return ( ) } function AvatarImage({ className, ...props }: React.ComponentProps) { return ( ) } function AvatarFallback({ className, ...props }: React.ComponentProps) { return ( ) } export { Avatar, AvatarImage, AvatarFallback } ``` ## Examples ### Avatar The canonical avatar — an image with an initials fallback that shows while it loads or if it fails. **Install:** ```bash npx shadcn@latest add @designrevision/avatar-demo-01 ``` **Dependencies:** avatar ```tsx // components/ui/avatar-demo-01.tsx import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar" export default function AvatarDemo01() { return ( CN ) } ``` --- ### Fallbacks Initials fallbacks for when there is no image — plain and colored. **Install:** ```bash npx shadcn@latest add @designrevision/avatar-fallback-01 ``` **Dependencies:** avatar ```tsx // components/ui/avatar-fallback-01.tsx import { Avatar, AvatarFallback } from "@/components/ui/avatar" const colored = [ { initials: "AL", color: "bg-rose-500" }, { initials: "BC", color: "bg-blue-500" }, { initials: "CD", color: "bg-emerald-500" }, { initials: "DE", color: "bg-violet-500" }, ] export default function AvatarFallbackDemo01() { return (
{/* Plain fallback (shows when there's no image). */} JD {/* Colored fallbacks. */} {colored.map((person) => ( {person.initials} ))}
) } ``` --- ### Sizes Avatars from `size-6` to `size-16` — set the size with a className on `Avatar`. **Install:** ```bash npx shadcn@latest add @designrevision/avatar-sizes-01 ``` **Dependencies:** avatar ```tsx // components/ui/avatar-sizes-01.tsx import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar" const sizes = ["size-6", "size-8", "size-10", "size-12", "size-16"] export default function AvatarSizes01() { return (
{sizes.map((size) => ( CN ))}
) } ``` --- ### Avatar group A stacked group of overlapping avatars (`-space-x` + ring) with a "+N" overflow. **Install:** ```bash npx shadcn@latest add @designrevision/avatar-group-01 ``` **Dependencies:** avatar ```tsx // components/ui/avatar-group-01.tsx import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar" const members = [ { src: "https://github.com/shadcn.png", fallback: "CN" }, { src: "https://github.com/leerob.png", fallback: "LR" }, { src: "https://github.com/evilrabbit.png", fallback: "ER" }, { src: "https://github.com/vercel.png", fallback: "VC" }, ] export default function AvatarGroup01() { return (
{members.map((member, index) => ( {member.fallback} ))} +3
) } ``` --- ### Status indicator An online / away / offline status dot pinned to the bottom-right of the avatar. **Install:** ```bash npx shadcn@latest add @designrevision/avatar-status-01 ``` **Dependencies:** avatar ```tsx // components/ui/avatar-status-01.tsx import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar" const people = [ { src: "https://github.com/shadcn.png", fallback: "CN", status: "bg-green-500", label: "Online" }, { src: "https://github.com/leerob.png", fallback: "LR", status: "bg-amber-500", label: "Away" }, { src: "https://github.com/evilrabbit.png", fallback: "ER", status: "bg-muted-foreground", label: "Offline" }, ] export default function AvatarStatus01() { return (
{people.map((person, index) => (
{person.fallback}
))}
) } ``` --- ### Shape & ring Round (default), square (`rounded-lg`), and a ringed avatar with `ring-offset`. **Install:** ```bash npx shadcn@latest add @designrevision/avatar-shapes-01 ``` **Dependencies:** avatar ```tsx // components/ui/avatar-shapes-01.tsx import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar" export default function AvatarShapes01() { return (
{/* Default round */} CN {/* Square (rounded-lg) */} CN {/* With a ring */} CN
) } ``` --- ### With name The user-row pattern — an avatar beside a name and email, the second falling back to initials. **Install:** ```bash npx shadcn@latest add @designrevision/avatar-detail-01 ``` **Dependencies:** avatar ```tsx // components/ui/avatar-detail-01.tsx import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar" const people = [ { src: "https://github.com/shadcn.png", name: "shadcn", email: "shadcn@example.com", fallback: "CN" }, { src: "", name: "Ada Lovelace", email: "ada@example.com", fallback: "AL" }, ] export default function AvatarDetail01() { return (
{people.map((person, index) => (
{person.fallback}

{person.name}

{person.email}

))}
) } ``` --- # Shadcn Badge > Free shadcn/ui badge component for React — default, secondary, destructive and outline variants, with icon and status badge examples. Copy or install in one command. Source: https://designrevision.com/components/badge The **Shadcn Badge** is a small, theme-aware label for status, categories, counts, and tags — a drop-in shadcn/ui badge built on `class-variance-authority` and styled entirely with CSS variables. It ships four variants and an `asChild` escape hatch for rendering as a link. ## When to use Reach for a badge to **annotate** other content — a status pill beside a name, a count on a [button](/components/button), a category tag on a card. For interactive actions, use a button instead; a badge is a label, not a control. ## Variants Four variants cover the common cases: `default` for emphasis, `secondary` for neutral labels, `destructive` for errors or warnings, and `outline` for a quiet, bordered style. Set them with the `variant` prop. Any SVG child is auto-sized to 12px and spaced from the text, so status badges with a leading icon need no extra classes. ## Theming The badge reads your design tokens (`--primary`, `--secondary`, `--destructive`), so it follows your theme — including dark mode — with zero overrides. ## Installation ### badge **Install:** ```bash npx shadcn@latest add @designrevision/badge ``` **Dependencies:** @radix-ui/react-slot, class-variance-authority, utils **Props** | Prop | Type | Default | Description | | --- | --- | --- | --- | | variant | 'default' \| 'secondary' \| 'destructive' \| 'outline' | default | Visual style of the badge. | | asChild | boolean | false | Render as the child element via Radix Slot — e.g. to wrap a link. | **Usage & accessibility:** Extends React.HTMLAttributes — all native span attributes are supported. ```tsx // components/ui/badge.tsx import * as React from "react" import { Slot } from "@radix-ui/react-slot" import { cva, type VariantProps } from "class-variance-authority" import { cn } from "@/lib/utils" const badgeVariants = cva( "inline-flex w-fit shrink-0 items-center justify-center gap-1 whitespace-nowrap rounded-md border px-2 py-0.5 text-xs font-medium transition-colors [&>svg]:pointer-events-none [&>svg]:size-3", { variants: { variant: { default: "border-transparent bg-primary text-primary-foreground", secondary: "border-transparent bg-secondary text-secondary-foreground", destructive: "border-transparent bg-destructive text-destructive-foreground", outline: "text-foreground", }, }, defaultVariants: { variant: "default", }, } ) export interface BadgeProps extends React.HTMLAttributes, VariantProps { asChild?: boolean } function Badge({ className, variant, asChild = false, ...props }: BadgeProps) { const Comp = asChild ? Slot : "span" return } export { Badge, badgeVariants } ``` ## Examples ### Variants The four built-in variants — default, secondary, destructive, and outline — plus status badges with a leading icon. **Install:** ```bash npx shadcn@latest add @designrevision/badge-variants-01 ``` **Dependencies:** lucide-react, badge ```tsx // components/ui/badge-variants-01.tsx import { BadgeCheck, Clock } from "lucide-react" import { Badge } from "@/components/ui/badge" export default function BadgeVariants01() { return (
Default Secondary Destructive Outline Verified Pending
) } ``` --- # Shadcn Bar Chart > Free shadcn/ui bar chart for React — built on Recharts and themed with CSS variables. Single and multiple series, stacked, horizontal, an interactive variant, plus loading, empty, and accessible states. Source: https://designrevision.com/components/bar-chart The **Shadcn Bar Chart** is a Recharts `BarChart` wrapped in the shadcn `ChartContainer`, so it's themed with your `--chart` CSS variables and dark-mode-ready out of the box. Below: single and multiple series, stacked, horizontal, an interactive variant, and the production states (loading, empty, accessible). ## The basic bar chart ```tsx const chartConfig = { desktop: { label: "Desktop", color: "var(--chart-1)" }, } satisfies ChartConfig } /> ``` ## Grouped & stacked Render one `` per series. With **no `stackId`** they group side by side; with a **shared `stackId`** they stack — round the outer corners with `radius`: ```tsx ``` ## Horizontal Flip the layout — `layout="vertical"`, a category `YAxis`, and a numeric (hidden) `XAxis`: ```tsx ``` ## Interactive The Interactive example tracks an active series in state, shows per-series totals in clickable header tiles, and points the `Bar` at the active key — the dashboard pattern for switching between metrics. ## Production states - **Loading** — a skeleton with bars at varying heights. - **Empty** — an icon + message when there's no data. - **Accessible** — `accessibilityLayer` (keyboard), `role="img"` + `aria-label`, and a visually-hidden data table. See **[Shadcn Charts](/components/chart)** for the theming system, dark mode, and the other chart types. ## Installation ### chart **Install:** ```bash npx shadcn@latest add @designrevision/chart ``` **Dependencies:** recharts, utils, card **Props** | Prop | Type | Default | Description | | --- | --- | --- | --- | | config * | ChartConfig | — | Maps each data key to { label, icon?, color }. Colors become --color- CSS vars. | | children * | React.ReactElement (a Recharts chart) | — | A single Recharts chart (BarChart, LineChart, …) — ChartContainer wraps it in a ResponsiveContainer. | `*` required. **Usage & accessibility:** A Recharts wrapper themed with CSS variables. Pass a `config` (ChartConfig: each data key → { label, icon?, color }) to ; it injects --color- vars (scoped per light/dark theme) that your Recharts series reference as fill="var(--color-desktop)". Use }> and }> for themed tooltips/legends. Colors default to the --chart-1..5 tokens, which re-theme automatically in dark mode. The container needs a height — keep aspect-video / min-h-* or ResponsiveContainer measures 0. This is the base primitive; the Bar/Line/Area/Pie/Radar/Radial chart pages compose it. ```tsx // components/ui/chart.tsx "use client" import * as React from "react" import * as RechartsPrimitive from "recharts" import { cn } from "@/lib/utils" // Format: { THEME_NAME: CSS_SELECTOR } const THEMES = { light: "", dark: ".dark" } as const export type ChartConfig = { [k in string]: { label?: React.ReactNode icon?: React.ComponentType } & ( | { color?: string; theme?: never } | { color?: never; theme: Record } ) } type ChartContextProps = { config: ChartConfig } const ChartContext = React.createContext(null) function useChart() { const context = React.useContext(ChartContext) if (!context) { throw new Error("useChart must be used within a ") } return context } function ChartContainer({ id, className, children, config, ...props }: React.ComponentProps<"div"> & { config: ChartConfig children: React.ComponentProps< typeof RechartsPrimitive.ResponsiveContainer >["children"] }) { const uniqueId = React.useId() const chartId = `chart-${id || uniqueId.replace(/:/g, "")}` return (
{children}
) } const ChartStyle = ({ id, config }: { id: string; config: ChartConfig }) => { const colorConfig = Object.entries(config).filter( ([, config]) => config.theme || config.color ) if (!colorConfig.length) { return null } return (