UIPackage
Menu

Framework

Change language

Boilerplate repo

Pricing Tier Comparison Matrix

blockmarketing

Comprehensive pricing tier comparison matrix with billing cycle discount toggles, prominent recommended plan, and granular capability matrix table.

Also available for Vue ->

Installation

$npx shadcn@latest add https://uipkge.dev/r/react/pricing-tier-comparison-matrix.json
Named registry:npx shadcn@latest add @uipkge-react/pricing-tier-comparison-matrixInstalls to:components/blocks/

Variants

Loading interactive previews…

Props

NameType / ValuesDefaultRequired
classNamestringoptional

Schema

Type aliases exported from this item's source. Use these to shape the data you pass in.

Plan
interface Plan {
  id: string
  name: string
  description: string
  monthlyPrice: number
  annualPrice: number
  highlight: boolean
  badge?: string
  ctaText: string
  ctaVariant: 'default' | 'outline' | 'secondary'
}
FeatureComparisonRow
interface FeatureComparisonRow {
  category: string
  features: {
    name: string
    tooltip: string
    community: boolean | string
    pro: boolean | string
    enterprise: boolean | string
  }[]
}

npm dependencies

Files installed (1)

  • components/blocks/PricingTierComparisonMatrix.tsx12.6 kB
    'use client'
    
    import * as React from 'react'
    import { ArrowRight, Check, Minus, ShieldCheck } from 'lucide-react'
    import { Badge } from '@/components/ui/badge'
    import { Button } from '@/components/ui/button'
    import { Card } from '@/components/ui/card'
    import { cn } from '@/lib/utils'
    
    type BillingCycle = 'monthly' | 'annual'
    
    interface Plan {
      id: string
      name: string
      description: string
      monthlyPrice: number
      annualPrice: number
      highlight: boolean
      badge?: string
      ctaText: string
      ctaVariant: 'default' | 'outline' | 'secondary'
    }
    
    const plans: Plan[] = [
      {
        id: 'community',
        name: 'Community OSS',
        description: 'For indie hackers and developers building open source products.',
        monthlyPrice: 0,
        annualPrice: 0,
        highlight: false,
        ctaText: 'Start Building Free',
        ctaVariant: 'outline',
      },
      {
        id: 'pro',
        name: 'Pro Team',
        description: 'For growing startup engineering teams that require high velocity.',
        monthlyPrice: 29,
        annualPrice: 24,
        highlight: true,
        badge: 'Most Popular',
        ctaText: 'Claim Pro License',
        ctaVariant: 'default',
      },
      {
        id: 'enterprise',
        name: 'Enterprise Scale',
        description: 'Dedicated registry syncing, SSO, SOC2 compliance audits, and SLA.',
        monthlyPrice: 119,
        annualPrice: 99,
        highlight: false,
        ctaText: 'Talk to Sales',
        ctaVariant: 'outline',
      },
    ]
    
    interface FeatureComparisonRow {
      category: string
      features: {
        name: string
        tooltip: string
        community: boolean | string
        pro: boolean | string
        enterprise: boolean | string
      }[]
    }
    
    const comparisonData: FeatureComparisonRow[] = [
      {
        category: 'Registry & Core Primitives',
        features: [
          {
            name: 'Full AST Component Source Access',
            tooltip: 'Raw SFC and TSX source files copied directly into your repository.',
            community: true,
            pro: true,
            enterprise: true,
          },
          {
            name: 'Dual-Framework Parity (Vue + React)',
            tooltip: 'Identical DOM semantics and CVA tokens across both ecosystems.',
            community: true,
            pro: true,
            enterprise: true,
          },
          {
            name: 'Curated Marketing & SaaS Blocks',
            tooltip: 'Access to 450+ production-grade unbundled layout blocks.',
            community: '100+ Blocks',
            pro: 'All 450+ Blocks',
            enterprise: 'All Blocks + Custom',
          },
          {
            name: 'Tailwind CSS v4 OKLCH Token System',
            tooltip: 'Hardware-calibrated color spaces and spring curves.',
            community: true,
            pro: true,
            enterprise: true,
          },
        ],
      },
      {
        category: 'Enterprise & Security Compliance',
        features: [
          {
            name: 'Private Registry Mirroring',
            tooltip: 'Host your organization’s customized internal registry behind a firewall.',
            community: false,
            pro: '1 Private Repo',
            enterprise: 'Unlimited Private Hubs',
          },
          {
            name: 'SOC2 & ISO 27001 Audit Packs',
            tooltip: 'Pre-certified security documentation and architecture proofs.',
            community: false,
            pro: false,
            enterprise: true,
          },
          {
            name: 'Guaranteed 99.99% Registry CDN SLA',
            tooltip: 'Global multi-region edge distribution uptime guarantee.',
            community: false,
            pro: '99.9% SLA',
            enterprise: '99.99% High Availability',
          },
          {
            name: 'Dedicated Design Engineering Support',
            tooltip: 'Direct Slack / Discord channel with core design system maintainers.',
            community: false,
            pro: 'Priority Email',
            enterprise: 'Dedicated Slack Channel',
          },
        ],
      },
    ]
    
    export interface PricingTierComparisonMatrixProps {
      className?: string
    }
    
    export function PricingTierComparisonMatrix({ className }: PricingTierComparisonMatrixProps) {
      const [billing, setBilling] = React.useState<BillingCycle>('annual')
    
      return (
        <section
          data-slot="pricing-tier-comparison-matrix"
          className={cn('bg-background relative overflow-hidden px-4 py-16 sm:px-6 sm:py-24 lg:px-8', className)}
        >
          <div className="mx-auto max-w-7xl space-y-16">
            {/* Header */}
            <div className="mx-auto max-w-3xl space-y-4 text-center">
              <Badge variant="secondary" className="gap-1.5 px-3 py-1 font-mono text-xs shadow-xs">
                <ShieldCheck className="text-primary size-3.5" />
                Predictable Pricing
              </Badge>
              <h2 className="text-foreground text-3xl font-bold tracking-tight sm:text-4xl">
                Zero seat taxes. Own your source code forever.
              </h2>
              <p className="text-muted-foreground text-base">
                Choose the tier that fits your engineering team's delivery scale and compliance needs.
              </p>
    
              {/* Billing Toggle */}
              <div className="flex items-center justify-center gap-3 pt-4">
                <span
                  className={cn(
                    'font-mono text-xs',
                    billing === 'monthly' ? 'text-foreground font-bold' : 'text-muted-foreground',
                  )}
                >
                  Monthly
                </span>
                <div className="border-border bg-card relative flex items-center rounded-full border p-1">
                  <button
                    type="button"
                    className={cn(
                      'relative z-10 rounded-full px-3 py-1 font-mono text-xs transition-colors',
                      billing === 'monthly'
                        ? 'bg-primary text-primary-foreground font-semibold shadow-xs'
                        : 'text-muted-foreground',
                    )}
                    onClick={() => setBilling('monthly')}
                  >
                    Monthly
                  </button>
                  <button
                    type="button"
                    className={cn(
                      'relative z-10 flex items-center gap-1.5 rounded-full px-3 py-1 font-mono text-xs transition-colors',
                      billing === 'annual'
                        ? 'bg-primary text-primary-foreground font-semibold shadow-xs'
                        : 'text-muted-foreground',
                    )}
                    onClick={() => setBilling('annual')}
                  >
                    <span>Annual</span>
                    <span className="bg-success rounded-full px-1.5 py-0.5 text-xs font-bold text-white">Save 20%</span>
                  </button>
                </div>
              </div>
            </div>
    
            {/* Pricing Plan Cards Grid (3 Columns) */}
            <div className="grid grid-cols-1 items-stretch gap-6 md:grid-cols-3">
              {plans.map((plan) => (
                <Card
                  key={plan.id}
                  className={cn(
                    'border-border bg-card relative flex flex-col justify-between space-y-6 rounded-2xl p-6 text-left shadow-xl transition-colors sm:p-8',
                    plan.highlight
                      ? 'border-primary/80 ring-primary/20 scale-[1.02] shadow-sm ring-2'
                      : 'hover:border-border/80',
                  )}
                >
                  {/* Top Badge */}
                  {plan.badge && (
                    <div className="absolute -top-3 left-1/2 -translate-x-1/2">
                      <Badge className="px-3 py-0.5 font-mono text-xs tracking-wider uppercase shadow-md">
                        {plan.badge}
                      </Badge>
                    </div>
                  )}
    
                  <div className="space-y-4">
                    <div>
                      <h3 className="text-foreground font-mono text-xl font-bold">{plan.name}</h3>
                      <p className="text-muted-foreground mt-1 min-h-[36px] text-xs">{plan.description}</p>
                    </div>
    
                    {/* Price display */}
                    <div className="flex items-baseline gap-1.5 font-mono">
                      <span className="text-foreground text-4xl font-bold">
                        ${billing === 'annual' ? plan.annualPrice : plan.monthlyPrice}
                      </span>
                      <span className="text-muted-foreground text-xs">/ month</span>
                    </div>
                    <p className="text-muted-foreground font-mono text-xs">
                      {billing === 'annual' && plan.annualPrice > 0
                        ? `Billed annually ($${plan.annualPrice * 12}/yr)`
                        : 'Billed monthly'}
                    </p>
                  </div>
    
                  <Button variant={plan.ctaVariant} className="h-10 w-full gap-1.5 font-mono text-xs shadow-xs">
                    <span>{plan.ctaText}</span>
                    <ArrowRight className="size-3.5" />
                  </Button>
                </Card>
              ))}
            </div>
    
            {/* Deep Feature Comparison Matrix Table */}
            <div className="space-y-6">
              <div className="text-center">
                <h3 className="text-foreground font-mono text-xl font-bold">Detailed Feature Comparison</h3>
                <p className="text-muted-foreground mt-1 text-xs">Full granular matrix of capabilities and entitlements.</p>
              </div>
    
              <Card className="border-border bg-card overflow-hidden rounded-2xl text-left shadow-sm">
                <div className="overflow-x-auto">
                  <table className="w-full border-collapse text-left text-xs">
                    <thead>
                      <tr className="border-border bg-muted/40 text-muted-foreground border-b font-mono">
                        <th className="w-1/2 p-4 font-semibold">Capability</th>
                        <th className="p-4 text-center font-semibold">Community</th>
                        <th className="text-primary p-4 text-center font-bold font-semibold">Pro Team</th>
                        <th className="p-4 text-center font-semibold">Enterprise</th>
                      </tr>
                    </thead>
                    <tbody>
                      {comparisonData.map((section, sIdx) => (
                        <React.Fragment key={sIdx}>
                          <tr className="bg-muted/20 border-border/80 border-b">
                            <td
                              colSpan={4}
                              className="text-muted-foreground p-3 px-4 font-mono text-xs font-bold tracking-wider uppercase"
                            >
                              {section.category}
                            </td>
                          </tr>
                          {section.features.map((row, rIdx) => (
                            <tr key={rIdx} className="border-border/60 hover:bg-muted/10 border-b transition-colors">
                              <td className="p-4">
                                <div className="text-foreground font-medium">{row.name}</div>
                                <div className="text-muted-foreground mt-0.5 text-xs">{row.tooltip}</div>
                              </td>
                              <td className="p-4 text-center font-mono">
                                {typeof row.community === 'boolean' ? (
                                  row.community ? (
                                    <Check className="text-success mx-auto size-4" />
                                  ) : (
                                    <Minus className="text-muted-foreground/40 mx-auto size-4" />
                                  )
                                ) : (
                                  <span className="text-muted-foreground">{row.community}</span>
                                )}
                              </td>
                              <td className="p-4 text-center font-mono font-semibold">
                                {typeof row.pro === 'boolean' ? (
                                  row.pro ? (
                                    <Check className="text-success mx-auto size-4" />
                                  ) : (
                                    <Minus className="text-muted-foreground/40 mx-auto size-4" />
                                  )
                                ) : (
                                  <span className="text-primary">{row.pro}</span>
                                )}
                              </td>
                              <td className="p-4 text-center font-mono">
                                {typeof row.enterprise === 'boolean' ? (
                                  row.enterprise ? (
                                    <Check className="text-success mx-auto size-4" />
                                  ) : (
                                    <Minus className="text-muted-foreground/40 mx-auto size-4" />
                                  )
                                ) : (
                                  <span className="text-foreground font-semibold">{row.enterprise}</span>
                                )}
                              </td>
                            </tr>
                          ))}
                        </React.Fragment>
                      ))}
                    </tbody>
                  </table>
                </div>
              </Card>
            </div>
          </div>
        </section>
      )
    }
    export default PricingTierComparisonMatrix
    

Raw manifest:https://uipkge.dev/r/react/pricing-tier-comparison-matrix.json