Docs
Feature Comparison

Feature Comparison

A competitive feature comparison table for comparing your product against competitors. Clean design with category grouping and flexible value types.

See how we compare to the competition

We offer more features, better performance, and superior support at a competitive price.

Core Features

Real-time collaboration

Work together seamlessly

Our Product:
Competitor A:
Competitor B:
Advanced analytics

Deep insights and reporting

Our Product:
Competitor A:
Limited
Competitor B:
Custom workflows

Automate your processes

Our Product:
Competitor A:
Competitor B:
API access

Full REST API

Our Product:
Unlimited
Competitor A:
Rate limited
Competitor B:
Basic

Integration

Third-party integrations
Our Product:
500+
Competitor A:
100+
Competitor B:
50+
Webhooks
Our Product:
Competitor A:
Competitor B:
Custom integrations
Our Product:
Competitor A:
Enterprise only
Competitor B:

Security

SSO & SAML
Our Product:
Competitor A:
Enterprise only
Competitor B:
2FA authentication
Our Product:
Competitor A:
Competitor B:
SOC 2 compliance
Our Product:
Competitor A:
Competitor B:
Audit logs
Our Product:
Unlimited
Competitor A:
90 days
Competitor B:
30 days

Support

24/7 support
Our Product:
Competitor A:
Business hours
Competitor B:
Email only
Dedicated account manager
Our Product:
Competitor A:
Enterprise only
Competitor B:
SLA guarantee
Our Product:
99.99%
Competitor A:
99.9%
Competitor B:
99%

Performance

Response time
Our Product:
<50ms
Competitor A:
<200ms
Competitor B:
<500ms
Uptime
Our Product:
99.99%
Competitor A:
99.9%
Competitor B:
99.5%
Global CDN
Our Product:
150+ locations
Competitor A:
50+ locations
Competitor B:
10+ locations

Features

  • Competitive comparison - Compare against 2 competitors
  • Winner indicators - Visual badges showing who wins each feature
  • Score summary - Crown icon and win count for the leader
  • Category grouping - Organize features by category
  • Tooltips - Hover info icons for additional context
  • Column headers - Beautiful headers with icons and badges
  • Flexible values - Boolean, string, or custom React nodes
  • Highlighted column - Mark your product as "That's us!"
  • Feature descriptions - Optional descriptions for clarity
  • Visual hierarchy - Winning rows highlighted with blue tint
  • Responsive design - Mobile-friendly with column selector
  • Hover effects - Smooth row highlighting
  • TypeScript support - Full type safety
  • Customizable - Easy to style and extend

Installation

Copy and paste the following code into your project.

import { Check, X, Minus, LucideIcon, Crown, Info } from "lucide-react";
import { ReactNode } from "react";
 
interface ComparisonColumn {
  name: string;
  description?: string;
  icon?: LucideIcon;
  highlighted?: boolean;
  badge?: string;
}
 
interface ComparisonFeature {
  category?: string;
  name: string;
  description?: string;
  tooltip?: string;
  yours: boolean | string | ReactNode;
  competitorA: boolean | string | ReactNode;
  competitorB: boolean | string | ReactNode;
  winner?: "yours" | "competitorA" | "competitorB" | "tie";
}
 
interface FeatureComparisonProps {
  headline?: string;
  description?: string;
  columns: {
    yours: ComparisonColumn;
    competitorA: ComparisonColumn;
    competitorB: ComparisonColumn;
  };
  features: ComparisonFeature[];
  showWinnerBadges?: boolean;
  showScoreSummary?: boolean;
}
 
export function FeatureComparison({
  headline = "See how we compare",
  description,
  columns,
  features,
  showWinnerBadges = true,
  showScoreSummary = true,
}: FeatureComparisonProps) {
  const scores = {
    yours: features.filter((f) => f.winner === "yours").length,
    competitorA: features.filter((f) => f.winner === "competitorA").length,
    competitorB: features.filter((f) => f.winner === "competitorB").length,
  };
 
  const renderValue = (
    value: boolean | string | ReactNode,
    isWinner: boolean = false,
  ) => {
    if (typeof value === "boolean") {
      return (
        <div
          className={`inline-flex items-center justify-center rounded-full p-1 ${
            isWinner && value ? "bg-emerald-500/10" : ""
          }`}
        >
          {value ? (
            <Check
              className={`h-5 w-5 ${
                isWinner ? "text-emerald-400" : "text-emerald-500/70"
              }`}
            />
          ) : (
            <X className="h-5 w-5 text-zinc-600" />
          )}
        </div>
      );
    }
    if (value === "-" || value === "") {
      return <Minus className="h-5 w-5 text-zinc-600" />;
    }
    return (
      <span
        className={`text-sm font-medium ${
          isWinner ? "text-white" : "text-zinc-400"
        }`}
      >
        {value}
      </span>
    );
  };
 
  const ColumnHeader = ({
    column,
    type,
    score,
  }: {
    column: ComparisonColumn;
    type: "yours" | "competitorA" | "competitorB";
    score?: number;
  }) => {
    const Icon = column.icon;
    const isYours = type === "yours";
    const isLeader =
      showScoreSummary && score === Math.max(...Object.values(scores));
 
    return (
      <div
        className={`relative rounded-2xl border p-6 transition-all ${
          column.highlighted || isYours
            ? "border-blue-500/50 bg-gradient-to-b from-blue-500/10 to-transparent"
            : "border-zinc-800/50 bg-zinc-950"
        }`}
      >
        {(column.badge || isYours) && (
          <div className="absolute -top-3 left-1/2 -translate-x-1/2">
            <span className="rounded-full bg-blue-500 px-3 py-1 text-xs font-semibold text-white">
              {column.badge || "That's us!"}
            </span>
          </div>
        )}
        <div className="text-center">
          {Icon && (
            <div className="mb-4 flex justify-center">
              <div
                className={`rounded-lg p-3 ${
                  isYours ? "bg-blue-500/10" : "bg-zinc-800/50"
                }`}
              >
                <Icon
                  className={`h-6 w-6 ${
                    isYours ? "text-blue-400" : "text-zinc-500"
                  }`}
                />
              </div>
            </div>
          )}
          <h3 className="text-xl font-bold text-white">{column.name}</h3>
          {column.description && (
            <p className="mt-2 text-sm text-zinc-500">{column.description}</p>
          )}
          {showScoreSummary && score !== undefined && (
            <div className="mt-4 flex items-center justify-center gap-2">
              {isLeader && <Crown className="h-4 w-4 text-yellow-500" />}
              <div className="rounded-full bg-zinc-900 px-3 py-1">
                <span className="text-sm font-bold text-white">{score}</span>
                <span className="ml-1 text-xs text-zinc-500">wins</span>
              </div>
            </div>
          )}
        </div>
      </div>
    );
  };
 
  const groupedFeatures = features.reduce(
    (acc, feature) => {
      const category = feature.category || "Features";
      if (!acc[category]) {
        acc[category] = [];
      }
      acc[category].push(feature);
      return acc;
    },
    {} as Record<string, ComparisonFeature[]>,
  );
 
  return (
    <section className="w-full bg-black py-12 sm:py-16 lg:py-20 z-30">
      <div className="container px-4 md:px-6">
        <div className="mx-auto max-w-3xl space-y-4 text-center">
          <h2 className="text-3xl font-bold tracking-tighter text-white sm:text-4xl md:text-5xl">
            {headline}
          </h2>
          {description && (
            <p className="text-lg text-zinc-400 md:text-xl">{description}</p>
          )}
        </div>
 
        <div className="mx-auto mt-12 max-w-7xl">
          <div className="hidden lg:grid lg:grid-cols-4 lg:gap-6">
            <div className="p-6" />
            <ColumnHeader
              column={columns.yours}
              type="yours"
              score={scores.yours}
            />
            <ColumnHeader
              column={columns.competitorA}
              type="competitorA"
              score={scores.competitorA}
            />
            <ColumnHeader
              column={columns.competitorB}
              type="competitorB"
              score={scores.competitorB}
            />
          </div>
 
          <div className="mt-8 overflow-hidden rounded-2xl border border-zinc-800/50 bg-zinc-950">
            <div className="border-b border-zinc-800/50 p-4 lg:hidden">
              <select className="w-full rounded-lg border border-zinc-800 bg-zinc-900 px-4 py-2 text-white">
                <option>{columns.yours.name}</option>
                <option>{columns.competitorA.name}</option>
                <option>{columns.competitorB.name}</option>
              </select>
            </div>
 
            {Object.entries(groupedFeatures).map(
              ([category, categoryFeatures]) => (
                <div key={category}>
                  <div className="border-b border-zinc-800/50 bg-zinc-900/50 px-6 py-3">
                    <h4 className="text-sm font-semibold uppercase tracking-wider text-zinc-400">
                      {category}
                    </h4>
                  </div>
                  <div className="divide-y divide-zinc-800/50">
                    {categoryFeatures.map((feature, index) => {
                      const hasWinner = feature.winner && showWinnerBadges;
                      return (
                        <div
                          key={index}
                          className={`group relative grid grid-cols-1 gap-4 p-6 transition-all lg:grid-cols-4 lg:items-center ${
                            hasWinner && feature.winner === "yours"
                              ? "bg-blue-500/5 hover:bg-blue-500/10"
                              : "hover:bg-zinc-900/30"
                          }`}
                        >
                          {hasWinner && feature.winner === "yours" && (
                            <div className="absolute -left-1 top-1/2 hidden h-full w-1 -translate-y-1/2 rounded-r bg-blue-500 lg:block" />
                          )}
                          <div className="flex items-start gap-2">
                            <div className="flex-1">
                              <h5 className="font-semibold text-white">
                                {feature.name}
                              </h5>
                              {feature.description && (
                                <p className="mt-1 text-sm text-zinc-500">
                                  {feature.description}
                                </p>
                              )}
                            </div>
                            {feature.tooltip && (
                              <div className="group/tooltip relative">
                                <Info className="h-4 w-4 text-zinc-600 transition-colors hover:text-zinc-400" />
                                <div className="pointer-events-none absolute left-1/2 top-full z-10 mt-2 hidden w-48 -translate-x-1/2 rounded-lg border border-zinc-800 bg-zinc-900 p-2 text-xs text-zinc-400 shadow-xl group-hover/tooltip:block">
                                  {feature.tooltip}
                                </div>
                              </div>
                            )}
                          </div>
                          <div
                            className={`flex items-center justify-center lg:justify-center ${
                              hasWinner && feature.winner === "yours"
                                ? "rounded-lg bg-blue-500/10 p-2"
                                : ""
                            }`}
                          >
                            <div className="mr-auto text-sm text-zinc-500 lg:hidden">
                              {columns.yours.name}:
                            </div>
                            {renderValue(
                              feature.yours,
                              feature.winner === "yours",
                            )}
                          </div>
                          <div
                            className={`flex items-center justify-center lg:justify-center ${
                              hasWinner && feature.winner === "competitorA"
                                ? "rounded-lg bg-zinc-800/30 p-2"
                                : ""
                            }`}
                          >
                            <div className="mr-auto text-sm text-zinc-500 lg:hidden">
                              {columns.competitorA.name}:
                            </div>
                            {renderValue(
                              feature.competitorA,
                              feature.winner === "competitorA",
                            )}
                          </div>
                          <div
                            className={`flex items-center justify-center lg:justify-center ${
                              hasWinner && feature.winner === "competitorB"
                                ? "rounded-lg bg-zinc-800/30 p-2"
                                : ""
                            }`}
                          >
                            <div className="mr-auto text-sm text-zinc-500 lg:hidden">
                              {columns.competitorB.name}:
                            </div>
                            {renderValue(
                              feature.competitorB,
                              feature.winner === "competitorB",
                            )}
                          </div>
                        </div>
                      );
                    })}
                  </div>
                </div>
              ),
            )}
          </div>
        </div>
      </div>
    </section>
  );
}

Usage

Basic Usage

import FeatureComparison from "@/components/ui/feature-comparison";
import { Zap, Building2, Users } from "lucide-react";
 
export default function Page() {
  return (
    <FeatureComparison
      headline="See how we compare"
      description="We offer more features at a better price."
      columns={{
        yours: {
          name: "Our Product",
          icon: Zap,
        },
        competitorA: {
          name: "Competitor A",
          icon: Building2,
        },
        competitorB: {
          name: "Competitor B",
          icon: Users,
        },
      }}
      features={[
        {
          name: "Real-time collaboration",
          yours: true,
          competitorA: true,
          competitorB: false,
        },
        {
          name: "API access",
          yours: "Unlimited",
          competitorA: "Limited",
          competitorB: false,
        },
      ]}
    />
  );
}

With Categories

<FeatureComparison
  columns={{
    yours: { name: "Us" },
    competitorA: { name: "Them A" },
    competitorB: { name: "Them B" },
  }}
  features={[
    {
      category: "Core Features",
      name: "Advanced analytics",
      yours: true,
      competitorA: "Limited",
      competitorB: false,
    },
    {
      category: "Core Features",
      name: "Custom workflows",
      yours: true,
      competitorA: false,
      competitorB: false,
    },
    {
      category: "Security",
      name: "SSO & SAML",
      yours: true,
      competitorA: "Enterprise only",
      competitorB: false,
    },
  ]}
/>

With Descriptions

<FeatureComparison
  columns={{
    yours: {
      name: "Our Product",
      description: "The complete solution",
      icon: Zap,
      highlighted: true,
    },
    competitorA: {
      name: "Competitor A",
      description: "Legacy platform",
      icon: Building2,
    },
    competitorB: {
      name: "Competitor B",
      description: "Basic features",
      icon: Users,
    },
  }}
  features={[
    {
      category: "Performance",
      name: "Response time",
      description: "Average API response time",
      yours: "<50ms",
      competitorA: "<200ms",
      competitorB: "<500ms",
    },
  ]}
/>

With Winner Indicators

<FeatureComparison
  columns={{
    yours: { name: "Our Product" },
    competitorA: { name: "Competitor A" },
    competitorB: { name: "Competitor B" },
  }}
  features={[
    {
      name: "API access",
      yours: "Unlimited",
      competitorA: "Limited",
      competitorB: "Basic",
      winner: "yours", // Highlights this row
    },
    {
      name: "24/7 support",
      yours: true,
      competitorA: "Business hours",
      competitorB: false,
      winner: "yours",
    },
  ]}
  showWinnerBadges={true} // Default: true
  showScoreSummary={true} // Default: true
/>

With Tooltips

<FeatureComparison
  columns={{
    yours: { name: "Us" },
    competitorA: { name: "Them A" },
    competitorB: { name: "Them B" },
  }}
  features={[
    {
      name: "Advanced analytics",
      description: "Deep insights and reporting",
      tooltip: "Custom dashboards, export reports, and real-time metrics",
      yours: true,
      competitorA: "Limited",
      competitorB: false,
      winner: "yours",
    },
  ]}
/>

Custom Badges

<FeatureComparison
  columns={{
    yours: {
      name: "Our Product",
      badge: "Best Value",
    },
    competitorA: {
      name: "Competitor A",
    },
    competitorB: {
      name: "Competitor B",
    },
  }}
  features={[...]}
/>

Props

FeatureComparisonProps

PropTypeDefaultDescription
headlinestring"See how we compare"Section headline
descriptionstringundefinedOptional description
columnsColumnsRequiredColumn configuration object
featuresComparisonFeature[]RequiredArray of features to compare
showWinnerBadgesbooleantrueShow visual winner indicators
showScoreSummarybooleantrueShow win count and crown icon

ComparisonColumn

PropTypeDefaultDescription
namestringRequiredColumn name
descriptionstringundefinedColumn description
iconLucideIconundefinedColumn icon
highlightedbooleanfalseHighlight column
badgestringundefinedCustom badge text

ComparisonFeature

PropTypeDefaultDescription
categorystring"Features"Feature category
namestringRequiredFeature name
descriptionstringundefinedFeature description
tooltipstringundefinedHover tooltip with extra info
yoursboolean | string | ReactNodeRequiredYour product value
competitorAboolean | string | ReactNodeRequiredCompetitor A value
competitorBboolean | string | ReactNodeRequiredCompetitor B value
winner"yours" | "competitorA" | "competitorB" | "tie"undefinedMark the winner of this feature

TypeScript Interface

interface FeatureComparisonProps {
  headline?: string;
  description?: string;
  columns: {
    yours: ComparisonColumn;
    competitorA: ComparisonColumn;
    competitorB: ComparisonColumn;
  };
  features: ComparisonFeature[];
  showWinnerBadges?: boolean;
  showScoreSummary?: boolean;
}
 
interface ComparisonColumn {
  name: string;
  description?: string;
  icon?: LucideIcon;
  highlighted?: boolean;
  badge?: string;
}
 
interface ComparisonFeature {
  category?: string;
  name: string;
  description?: string;
  tooltip?: string;
  yours: boolean | string | ReactNode;
  competitorA: boolean | string | ReactNode;
  competitorB: boolean | string | ReactNode;
  winner?: "yours" | "competitorA" | "competitorB" | "tie";
}

Value Types

Features support three value types:

  • Boolean: Renders checkmark (✓) or X (✗)
  • String: Displays as text (e.g., "Unlimited", "Enterprise only")
  • ReactNode: Custom JSX for complex values

Advanced Features

Winner Indicators

Set the winner field on any feature to visually highlight who wins:

  • Blue left border on winning rows
  • Highlighted background for your wins
  • Brighter text for winning values
  • Dimmed text for losing values

Score Summary

When enabled (default), shows:

  • Crown icon (👑) on the column with most wins
  • Win count badge on each column header
  • Automatic calculation of scores

Tooltips

Add a tooltip field to any feature to show additional information on hover. Perfect for explaining complex features or providing context.

Category Grouping

Features are automatically grouped by the category field. Categories appear as section headers in the table. If no category is specified, features are grouped under "Features".

Responsive Behavior

  • Desktop (lg+): Side-by-side comparison with column headers
  • Mobile: Stacked layout with column selector dropdown

Use Cases

Perfect for:

  • Competitive analysis pages
  • Product comparison tables
  • "Why choose us" sections
  • Feature differentiation
  • Sales enablement
  • Marketing landing pages
  • Competitor benchmarking
  • Product positioning