Pricing Comparison
A complete pricing comparison section, ready to adapt to your product.
Make it yours.
Install the editable source and its dependencies into your React + Tailwind project.
Scroll horizontally for long lines
pnpm dlx shadcn@4.0.8 add http://localhost:3000/r/pricing-comparison.jsonUse it
Scroll horizontally for long lines
"use client";
import { PricingComparison } from "@/components/jez-ui/blocks/pricing-comparison";
export default function Example() {
return (
<>
<PricingComparison />
</>
);
}
Compose your own
These styled parts are included in the same install. Use children to build your layout; add className only when you want an override.
Scroll horizontally for long lines
"use client";
import {
PricingComparison,
PricingComparisonTable,
} from "@/components/jez-ui/blocks/pricing-comparison";
export default function Example() {
return (
<PricingComparison>
<PricingComparisonTable />
</PricingComparison>
);
}
Scroll horizontally for long lines
import { PricingComparison, PricingComparisonList, PricingComparisonTable, PricingComparisonTableHeader, PricingComparisonRow, PricingComparisonHead, PricingComparisonTableBody, PricingComparisonCell } from "@/components/jez-ui/blocks/pricing-comparison";Props & types
Scroll horizontally for long lines
export type PricingComparisonOptions = {
className?: string;
plans?: typeof PricingComparisonDefaultPlans;
features?: typeof PricingComparisonDefaultFeatures;
};
export type PricingComparisonProps = Omit<
React.ComponentProps<"div">,
keyof PricingComparisonOptions
> &
PricingComparisonOptions;
PricingComparisonPropsSource
View 2 source files
registry/blocks/pricing-comparison.tsx
Scroll horizontally for long lines
"use client";
import * as React from "react";
import { cn } from "../ui/utils";
export type PricingComparisonOptions = {
className?: string;
plans?: typeof PricingComparisonDefaultPlans;
features?: typeof PricingComparisonDefaultFeatures;
};
export type PricingComparisonProps = Omit<
React.ComponentProps<"div">,
keyof PricingComparisonOptions
> &
PricingComparisonOptions;
const PricingComparisonDefaultPlans = [
"Included",
"Personal",
"Team",
"Studio",
];
const PricingComparisonDefaultFeatures = [
["Projects", "3", "Unlimited", "Unlimited"],
["Guests", "1", "10", "Unlimited"],
["Version history", "7 days", "90 days", "Unlimited"],
["Support", "Community", "Email", "Priority"],
];
function usePricingComparisonModel({
plans = PricingComparisonDefaultPlans,
features = PricingComparisonDefaultFeatures,
className,
children,
...rootProps
}: PricingComparisonProps) {
return { plans, features, className, children, rootProps };
}
const PricingComparisonCompositionContext = React.createContext<ReturnType<
typeof usePricingComparisonModel
> | null>(null);
function usePricingComparisonComposition() {
const context = React.useContext(PricingComparisonCompositionContext);
if (!context)
throw new Error(
"PricingComparison parts must be inside PricingComparison.",
);
return context;
}
export function PricingComparison(props: PricingComparisonProps) {
const model = usePricingComparisonModel(props);
const { className, rootProps, children } = model;
return (
<PricingComparisonCompositionContext.Provider value={model}>
<div {...rootProps} className={cn("overflow-x-auto py-8", className)}>
{children !== undefined ? children : <PricingComparisonTable />}
</div>
</PricingComparisonCompositionContext.Provider>
);
}
export function PricingComparisonList({
className,
...props
}: React.ComponentProps<"table">) {
return (
<table
data-slot="pricing-comparison-list"
className={cn("w-full text-left text-sm", className)}
{...props}
/>
);
}
export function PricingComparisonTable({
children,
...props
}: Partial<React.ComponentProps<typeof PricingComparisonList>> & {
children?: React.ReactNode;
}) {
const { plans, features } = usePricingComparisonComposition();
return (
<PricingComparisonList {...props}>
{children === undefined ? (
<>
<caption className="mb-5 text-left font-display text-3xl">
Find your fit.
</caption>
<PricingComparisonTableHeader>
<PricingComparisonRow>
{plans.map((h) => (
<PricingComparisonHead key={h}>{h}</PricingComparisonHead>
))}
</PricingComparisonRow>
</PricingComparisonTableHeader>
<PricingComparisonTableBody>
{features.map((r) => (
<PricingComparisonRow
key={r[0]}
className="border-t border-border"
>
{r.map((v, i) => (
<PricingComparisonCell key={i}>{v}</PricingComparisonCell>
))}
</PricingComparisonRow>
))}
</PricingComparisonTableBody>
</>
) : (
children
)}
</PricingComparisonList>
);
}
export function PricingComparisonTableHeader({
className,
...props
}: React.ComponentProps<"thead">) {
return (
<thead
data-slot="pricing-comparison-tableheader"
className={cn("", className)}
{...props}
/>
);
}
export function PricingComparisonRow({
className,
...props
}: React.ComponentProps<"tr">) {
return (
<tr
data-slot="pricing-comparison-row"
className={cn("", className)}
{...props}
/>
);
}
export function PricingComparisonHead({
className,
...props
}: React.ComponentProps<"th">) {
return (
<th
data-slot="pricing-comparison-head"
className={cn("p-3", className)}
{...props}
/>
);
}
export function PricingComparisonTableBody({
className,
...props
}: React.ComponentProps<"tbody">) {
return (
<tbody
data-slot="pricing-comparison-tablebody"
className={cn("", className)}
{...props}
/>
);
}
export function PricingComparisonCell({
className,
...props
}: React.ComponentProps<"td">) {
return (
<td
data-slot="pricing-comparison-cell"
className={cn("p-3", className)}
{...props}
/>
);
}
registry/ui/utils.ts
Scroll horizontally for long lines
import { clsx, type ClassValue } from "clsx";
import { twMerge } from "tailwind-merge";
export function cn(...values: ClassValue[]) {
return twMerge(clsx(values));
}
Dependencies
clsx@2.1.1 · tailwind-merge@3.5.0
Accessibility & behaviour
Provide meaningful labels and preserve keyboard focus styles. Check contrast when customising theme colours.
Read the accessibility and performance guide →