Comparison table
An us versus them table with a sticky header, a highlighted column, and a stacked phone view.
pnpm dlx shadcn@latest add @uiarc/comparison-table- Showing how your product stacks up against named alternatives.
- Feature matrices with three to five columns and grouped rows.
- Use plan-comparison to compare your own pricing tiers.
- Use a sortable data table for large datasets.
Installation
Add Comparison table with the shadcn CLI, or copy the source by hand.
pnpm dlx shadcn@latest add @uiarc/comparison-tableAdds the block and its local dependencies, and installs motion, lucide-react. First time? Add the @uiarc registry to components.json, or use the full URL:
Usage
Use this on a pricing or alternatives page. Describe your columns and feature rows in comparison-table-data.ts, mark your product with highlight, and point the cta at signup; stickyTop offsets the header under a fixed site header.
import { ComparisonTable } from "@/registry/blocks/comparison-table/comparison-table"; export function Alternatives() { return ( <ComparisonTable columns={[ { id: "us", name: "Relay", caption: "$10 per seat", highlight: true }, { id: "them", name: "Legacy suite", caption: "$24 per seat" }, ]} sections={[{ id: "core", title: "Core", rows: [ { id: "offline", feature: "Offline mode", values: { us: true, them: false } }, { id: "sso", feature: "SSO", values: { us: true, them: { value: "partial", note: "Enterprise plan" } } }, ] }]} cta={{ label: "Start free trial", href: "/signup" }} stickyTop={64} /> );}ComparisonTable
An us versus them table with sections, check, partial and not included marks, a tinted band behind your column, a sticky frosted header, a differences only switch, and a stacked two column view on phones.
columnsComparisonColumn[]–Products to compare. Mark yours with highlight: true.sectionsComparisonSection[]–Grouped feature rows. Values: true, false, "partial", a string, or { value, note }.titlestring"How Relay compares"Section heading.descriptionstring–Line under the heading.differencesOnlyboolean–Hide rows where all visible columns match (controlled).defaultDifferencesOnlybooleanfalseInitial switch state when uncontrolled.onDifferencesOnlyChange(value: boolean) => void–Called when the switch flips.compareWithstring–Competitor shown beside yours in the stacked phone view (controlled).onCompareWithChange(id: string) => void–Called when a competitor is picked on phones.cta{ label: string; href?: string; onClick?: () => void; doneLabel?: string }–Button at the foot of your column. doneLabel morphs in after a click.stickyTopnumber0Offset for the sticky header, such as a fixed site header height.maxHeightnumber | string–Caps the table height and scrolls it inside the block with the header pinned.stackBelownumber640Block width below which the phone view takes over.classNamestring–Extra class on the section.- Tab
- Moves to the differences switch, the phone competitor picker, and the call to action.
- Space
- Flips the differences switch.
- Built with table, row, columnheader, rowheader and cell roles, so screen readers navigate it as a table.
- Marks have text alternatives: Included, Partial (with its note), Not included.
- The switch is a real checkbox with role switch; a polite live region says when rows are filtered.
- Meaning never relies on color: checks, half circles and dashes differ in shape, with a legend.
- Checks in your column draw in once, staggered down the table, when they scroll into view.
- Differences only collapses matching rows on the smooth spring and closes the gap.
- Picking a competitor on phones swaps its header and marks with a small pop.
- The call to action morphs its label to the done state.
- Reduced motion shows every mark at once and swaps collapses for instant changes.
- Below 640px (stackBelow) it shows your column beside one competitor with a picker above.
- Column widths are fractions, so it never scrolls sideways.
- A width observer switches layouts; no per frame work while scrolling.
- Row collapses animate height only on rows that change.
Notes for AI
Give your coding assistant the Markdown reference instead of screenshots.
- Use this for a /compare or /alternatives page or under pricing.
- Keep competitor names generic or factual; values should be verifiable.
- Set stickyTop to your fixed header height; use maxHeight only when embedding the table in a panel.
The full library index for assistants is at /llms.txt.