AIEO-ready websites that get cited by AITalk to us

Plan Feature Comparison Table

A responsive, semantic feature comparison table across three plans with grouped rows, tick and dash indicators and screen-reader labels so visitors can weigh every feature side by side.

React + TailwindNo dependenciespricingcomparisontableresponsive
Original · free to use
npx shadcn@latest add https://webinnoventix.com/r/pricing-comparison-table.json
pricing-comparison-table.tsx
export default function PricingComparisonTable() {
  const plans = [
    { name: "Starter", price: "£0", cadence: "free forever", featured: false },
    { name: "Growth", price: "£29", cadence: "per month", featured: true },
    { name: "Enterprise", price: "Custom", cadence: "talk to us", featured: false },
  ];

  const groups = [
    {
      name: "Core",
      rows: [
        { label: "Active projects", values: ["3", "Unlimited", "Unlimited"] },
        { label: "Team members", values: ["1", "10", "Unlimited"] },
        { label: "Storage", values: ["5 GB", "250 GB", "5 TB"] },
      ],
    },
    {
      name: "Collaboration",
      rows: [
        { label: "Shared workspaces", values: [true, true, true] },
        { label: "Real-time editing", values: [false, true, true] },
        { label: "Guest access", values: [false, true, true] },
      ],
    },
    {
      name: "Security and support",
      rows: [
        { label: "Two-factor authentication", values: [true, true, true] },
        { label: "Single sign-on", values: [false, false, true] },
        { label: "Audit logs", values: [false, true, true] },
        { label: "Priority support", values: [false, true, true] },
      ],
    },
  ];

  const renderValue = (value: string | boolean) => {
    if (typeof value === "string") {
      return <span className="text-sm font-medium text-zinc-900 dark:text-white">{value}</span>;
    }
    if (value) {
      return (
        <>
          <svg
            className="mx-auto h-5 w-5 text-emerald-600 dark:text-emerald-400"
            viewBox="0 0 20 20"
            fill="currentColor"
            aria-hidden="true"
          >
            <path
              fillRule="evenodd"
              d="M16.7 5.3a1 1 0 0 1 0 1.4l-7.5 7.5a1 1 0 0 1-1.4 0L3.3 10.7a1 1 0 1 1 1.4-1.4l3.8 3.8 6.8-6.8a1 1 0 0 1 1.4 0Z"
              clipRule="evenodd"
            />
          </svg>
          <span className="sr-only">Included</span>
        </>
      );
    }
    return (
      <>
        <svg
          className="mx-auto h-4 w-4 text-zinc-300 dark:text-zinc-600"
          viewBox="0 0 20 20"
          fill="currentColor"
          aria-hidden="true"
        >
          <path d="M4 9h12a1 1 0 1 1 0 2H4a1 1 0 1 1 0-2Z" />
        </svg>
        <span className="sr-only">Not included</span>
      </>
    );
  };

  return (
    <section className="bg-white px-6 py-16 dark:bg-zinc-950 md:py-24">
      <div className="mx-auto max-w-5xl">
        <div className="mb-10 text-center">
          <h2 className="text-3xl font-bold tracking-tight text-zinc-900 sm:text-4xl dark:text-white">
            Compare every plan
          </h2>
          <p className="mt-3 text-zinc-600 dark:text-zinc-400">
            See exactly what is included so you can pick the plan that fits.
          </p>
        </div>

        <div className="overflow-x-auto rounded-2xl border border-zinc-200 dark:border-zinc-800">
          <table className="w-full min-w-[40rem] border-collapse text-left">
            <caption className="sr-only">
              Feature comparison across the Starter, Growth and Enterprise plans
            </caption>
            <thead>
              <tr>
                <th
                  scope="col"
                  className="w-1/3 bg-zinc-50 p-5 align-bottom dark:bg-zinc-900/60"
                >
                  <span className="text-sm font-medium text-zinc-500 dark:text-zinc-400">
                    Plans
                  </span>
                </th>
                {plans.map((plan) => (
                  <th
                    key={plan.name}
                    scope="col"
                    className={
                      "p-5 align-bottom " +
                      (plan.featured
                        ? "bg-indigo-50 dark:bg-indigo-500/10"
                        : "bg-zinc-50 dark:bg-zinc-900/60")
                    }
                  >
                    <div className="flex flex-col gap-2">
                      <span className="flex items-center gap-2 text-base font-semibold text-zinc-900 dark:text-white">
                        {plan.name}
                        {plan.featured && (
                          <span className="rounded-full bg-indigo-600 px-2 py-0.5 text-xs font-semibold text-white">
                            Popular
                          </span>
                        )}
                      </span>
                      <span>
                        <span className="text-2xl font-bold tracking-tight text-zinc-900 dark:text-white">
                          {plan.price}
                        </span>{" "}
                        <span className="text-xs font-normal text-zinc-500 dark:text-zinc-400">
                          {plan.cadence}
                        </span>
                      </span>
                      <a
                        href="#"
                        className={
                          "mt-1 rounded-lg px-3 py-2 text-center text-xs font-semibold transition focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600 " +
                          (plan.featured
                            ? "bg-indigo-600 text-white hover:bg-indigo-500"
                            : "bg-zinc-900 text-white hover:bg-zinc-700 dark:bg-white dark:text-zinc-900 dark:hover:bg-zinc-200")
                        }
                      >
                        Choose {plan.name}
                      </a>
                    </div>
                  </th>
                ))}
              </tr>
            </thead>

            {groups.map((group) => (
              <tbody key={group.name}>
                <tr>
                  <th
                    scope="colgroup"
                    colSpan={4}
                    className="border-t border-zinc-200 bg-white px-5 pb-2 pt-6 text-xs font-semibold uppercase tracking-wide text-zinc-500 dark:border-zinc-800 dark:bg-zinc-950 dark:text-zinc-400"
                  >
                    {group.name}
                  </th>
                </tr>
                {group.rows.map((row) => (
                  <tr key={row.label} className="border-t border-zinc-100 dark:border-zinc-800/70">
                    <th
                      scope="row"
                      className="p-5 text-sm font-normal text-zinc-700 dark:text-zinc-300"
                    >
                      {row.label}
                    </th>
                    {row.values.map((value, index) => (
                      <td
                        key={plans[index].name}
                        className={
                          "p-5 text-center " +
                          (plans[index].featured ? "bg-indigo-50/50 dark:bg-indigo-500/5" : "")
                        }
                      >
                        {renderValue(value)}
                      </td>
                    ))}
                  </tr>
                ))}
              </tbody>
            ))}
          </table>
        </div>
      </div>
    </section>
  );
}

Built by Web Innoventix. Free for personal and commercial use, no attribution required.

Built by Web Innoventix

Need a custom interface, a full website, or SEO that gets you cited by AI? We design, build and rank it end to end.

Get a free quote

More blocks

All components
Chat with us