Skip to content
← Blocks

Pricing Comparison

A complete pricing comparison section, ready to adapt to your product.

Open ↗

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.json

Use 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";
Read the composition guide →

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;

PricingComparisonProps

Source

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 →