mdxui

Pricing Section

Pricing tiers and plans with feature comparison

Pricing Section

The <Pricing> section displays pricing tiers and plans in a clear, scannable format. It helps visitors compare options and choose the right plan for their needs.

Interface

import type { PricingProps } from 'mdxui'

interface PricingProps {
  /** Section title */
  title?: string
  /** Section description */
  description?: string
  /** Pricing tiers */
  tiers: PricingTier[]
  /** Show annual/monthly toggle */
  showToggle?: boolean
}

interface PricingTier {
  /** Tier name */
  name: string
  /** Price display (e.g., "$29/mo") */
  price: string
  /** Tier description */
  description?: string
  /** Feature list */
  features: string[]
  /** CTA button */
  cta: CTA
  /** Whether this tier is highlighted */
  highlighted?: boolean
}

interface CTA {
  text: string
  href: string
}

Usage

Basic Example

import { Pricing } from '@mdxui/beacon'

<Pricing
  tiers={[
    {
      name: "Starter",
      price: "$29/mo",
      features: [
        "Up to 10 users",
        "10GB storage",
        "Email support"
      ],
      cta: { text: "Start Free Trial", href: "/signup?plan=starter" }
    },
    {
      name: "Pro",
      price: "$99/mo",
      features: [
        "Unlimited users",
        "100GB storage",
        "Priority support",
        "Advanced analytics"
      ],
      cta: { text: "Start Free Trial", href: "/signup?plan=pro" },
      highlighted: true
    }
  ]}
/>

With Title and Description

<Pricing
  title="Simple, transparent pricing"
  description="Choose the plan that's right for your team. All plans include a 14-day free trial."
  tiers={[...]}
/>

With Annual/Monthly Toggle

<Pricing
  title="Pricing that scales with you"
  showToggle={true}
  tiers={[
    {
      name: "Starter",
      price: "$29/mo",
      description: "Perfect for small teams",
      features: [
        "Up to 5 team members",
        "5GB storage",
        "Basic analytics",
        "Email support"
      ],
      cta: { text: "Get Started", href: "/signup?plan=starter" }
    },
    {
      name: "Professional",
      price: "$99/mo",
      description: "For growing businesses",
      features: [
        "Up to 25 team members",
        "50GB storage",
        "Advanced analytics",
        "Priority support",
        "Custom integrations"
      ],
      cta: { text: "Get Started", href: "/signup?plan=pro" },
      highlighted: true
    },
    {
      name: "Enterprise",
      price: "Custom",
      description: "For large organizations",
      features: [
        "Unlimited team members",
        "Unlimited storage",
        "Advanced security",
        "Dedicated support",
        "Custom SLA",
        "On-premise option"
      ],
      cta: { text: "Contact Sales", href: "/contact" }
    }
  ]}
/>

Three-Tier Layout

<Pricing
  title="Find your perfect plan"
  tiers={[
    {
      name: "Free",
      price: "$0",
      description: "For individuals and hobby projects",
      features: [
        "1 user",
        "1GB storage",
        "Community support"
      ],
      cta: { text: "Sign Up Free", href: "/signup?plan=free" }
    },
    {
      name: "Team",
      price: "$49/mo",
      description: "For small teams and startups",
      features: [
        "Up to 10 users",
        "20GB storage",
        "Email support",
        "Basic analytics"
      ],
      cta: { text: "Try Free for 14 Days", href: "/signup?plan=team" },
      highlighted: true
    },
    {
      name: "Business",
      price: "$199/mo",
      description: "For larger teams and businesses",
      features: [
        "Unlimited users",
        "200GB storage",
        "Priority support",
        "Advanced analytics",
        "Custom integrations",
        "SSO"
      ],
      cta: { text: "Try Free for 14 Days", href: "/signup?plan=business" }
    }
  ]}
/>

Props

title (optional)

Type: string

Section heading displayed above pricing tiers.

<Pricing
  title="Pricing that works for everyone"
  tiers={[...]}
/>

description (optional)

Type: string

Supporting text below the title. Often mentions free trials or guarantees.

<Pricing
  title="Simple pricing"
  description="All plans include a 14-day free trial. No credit card required."
  tiers={[...]}
/>

tiers (required)

Type: PricingTier[]

Array of pricing tier objects.

interface PricingTier {
  name: string              // Plan name (e.g., "Pro", "Enterprise")
  price: string             // Price display (e.g., "$29/mo", "Custom")
  description?: string      // Plan description/subtitle
  features: string[]        // List of included features
  cta: CTA                  // Call-to-action button
  highlighted?: boolean     // Highlight this tier (most popular)
}

<Pricing
  tiers={[
    {
      name: "Starter",
      price: "$29/mo",
      description: "For small teams",
      features: ["10 users", "10GB storage"],
      cta: { text: "Get Started", href: "/signup" },
      highlighted: false
    }
  ]}
/>

showToggle (optional)

Type: boolean Default: false

Show a toggle to switch between monthly and annual pricing.

<Pricing
  showToggle={true}
  tiers={[...]}
/>

Note: When enabled, the implementation handles price switching. You may need to provide both monthly and annual prices in your data structure.


Tier Props

name (required)

Type: string

The plan name displayed prominently at the top of each tier.

{ name: "Professional" }
{ name: "Enterprise" }
{ name: "Free" }

price (required)

Type: string

Price display string. Can be a number, range, or custom text.

{ price: "$29/mo" }
{ price: "$99/month" }
{ price: "Custom" }
{ price: "Contact Sales" }
{ price: "$0" }

description (optional)

Type: string

Subtitle explaining who the plan is for.

{ description: "Perfect for small teams" }
{ description: "For growing businesses" }
{ description: "Enterprise-grade solution" }

features (required)

Type: string[]

List of features included in the tier. Keep them concise and scannable.

{
  features: [
    "Up to 10 users",
    "10GB storage",
    "Email support",
    "Basic analytics",
    "API access"
  ]
}

cta (required)

Type: CTA

Call-to-action button for this tier.

interface CTA {
  text: string    // Button text
  href: string    // Destination URL
}

{
  cta: {
    text: "Start Free Trial",
    href: "/signup?plan=pro"
  }
}

highlighted (optional)

Type: boolean Default: false

Visually emphasize this tier (usually "most popular").

{
  name: "Pro",
  highlighted: true  // Adds border, badge, or background
}

Implementation Details

The reference implementation from @mdxui/beacon includes:

Features

  • Responsive Grid - Stacks on mobile, grid on desktop
  • Highlighted Tier - Visual emphasis for recommended plan
  • Feature Lists - Clean, scannable feature lists
  • CTA Buttons - Prominent call-to-action per tier
  • Toggle Support - Monthly/annual pricing switcher
  • Accessibility - Semantic HTML and ARIA labels

Structure

<section className="pricing">
  {(title || description) && (
    <div className="pricing-header">
      {title && <h2>{title}</h2>}
      {description && <p>{description}</p>}
    </div>
  )}

  {showToggle && (
    <div className="pricing-toggle">
      <button>Monthly</button>
      <button>Annual</button>
    </div>
  )}

  <div className="pricing-grid">
    {tiers.map(tier => (
      <div
        key={tier.name}
        className={tier.highlighted ? 'tier highlighted' : 'tier'}
      >
        {tier.highlighted && (
          <span className="badge">Most Popular</span>
        )}

        <h3>{tier.name}</h3>
        {tier.description && <p>{tier.description}</p>}

        <div className="price">{tier.price}</div>

        <ul className="features">
          {tier.features.map(feature => (
            <li key={feature}>{feature}</li>
          ))}
        </ul>

        <Button href={tier.cta.href}>
          {tier.cta.text}
        </Button>
      </div>
    ))}
  </div>
</section>

Real-World Examples

SaaS Pricing

<Pricing
  title="Choose your plan"
  description="All plans include 14-day free trial. No credit card required."
  showToggle={true}
  tiers={[
    {
      name: "Starter",
      price: "$29/mo",
      description: "For individuals and small teams",
      features: [
        "Up to 5 team members",
        "5GB file storage",
        "Basic reporting",
        "Email support",
        "Mobile app access"
      ],
      cta: { text: "Start Free Trial", href: "/signup?plan=starter" }
    },
    {
      name: "Professional",
      price: "$99/mo",
      description: "For growing businesses",
      features: [
        "Up to 25 team members",
        "50GB file storage",
        "Advanced reporting & analytics",
        "Priority email & chat support",
        "API access",
        "Custom integrations",
        "Advanced permissions"
      ],
      cta: { text: "Start Free Trial", href: "/signup?plan=pro" },
      highlighted: true
    },
    {
      name: "Enterprise",
      price: "Custom",
      description: "For large organizations",
      features: [
        "Unlimited team members",
        "Unlimited storage",
        "Custom reporting",
        "Dedicated account manager",
        "99.99% SLA",
        "SSO & SAML",
        "On-premise deployment",
        "Custom contracts"
      ],
      cta: { text: "Contact Sales", href: "/contact" }
    }
  ]}
/>

Developer Platform Pricing

<Pricing
  title="Pay for what you use"
  description="Start free, scale as you grow"
  tiers={[
    {
      name: "Hobby",
      price: "$0",
      description: "For personal projects",
      features: [
        "100,000 API calls/mo",
        "1GB bandwidth",
        "Community support",
        "Public repos only"
      ],
      cta: { text: "Get Started Free", href: "/signup" }
    },
    {
      name: "Pro",
      price: "$49/mo",
      description: "For professional developers",
      features: [
        "1M API calls/mo",
        "10GB bandwidth",
        "Email support",
        "Private repos",
        "Advanced analytics",
        "Priority builds"
      ],
      cta: { text: "Start Free Trial", href: "/signup?plan=pro" },
      highlighted: true
    },
    {
      name: "Team",
      price: "$199/mo",
      description: "For development teams",
      features: [
        "10M API calls/mo",
        "100GB bandwidth",
        "Priority support",
        "Unlimited private repos",
        "Team collaboration",
        "Custom domains",
        "SSO"
      ],
      cta: { text: "Start Free Trial", href: "/signup?plan=team" }
    }
  ]}
/>

Content Platform Pricing

<Pricing
  title="Pricing for creators"
  tiers={[
    {
      name: "Free",
      price: "$0",
      features: [
        "Up to 100 subscribers",
        "Basic email templates",
        "Limited analytics"
      ],
      cta: { text: "Start Free", href: "/signup" }
    },
    {
      name: "Creator",
      price: "$15/mo",
      description: "Most popular",
      features: [
        "Up to 1,000 subscribers",
        "Custom email templates",
        "Full analytics",
        "No platform fees",
        "Custom domain"
      ],
      cta: { text: "Start Free Trial", href: "/signup?plan=creator" },
      highlighted: true
    },
    {
      name: "Business",
      price: "$49/mo",
      features: [
        "Up to 10,000 subscribers",
        "Advanced automation",
        "A/B testing",
        "Priority support",
        "API access",
        "Remove branding"
      ],
      cta: { text: "Start Free Trial", href: "/signup?plan=business" }
    }
  ]}
/>

Pricing Toggle

Implementation

The toggle switches between monthly and annual pricing:

const [interval, setInterval] = useState<'monthly' | 'annual'>('monthly')

<div className="pricing-toggle">
  <button
    className={interval === 'monthly' ? 'active' : ''}
    onClick={() => setInterval('monthly')}
  >
    Monthly
  </button>
  <button
    className={interval === 'annual' ? 'active' : ''}
    onClick={() => setInterval('annual')}
  >
    Annual
    <span className="badge">Save 20%</span>
  </button>
</div>

Data Structure

Provide both monthly and annual prices:

const tiers = [
  {
    name: "Pro",
    monthlyPrice: "$29/mo",
    annualPrice: "$24/mo",
    annualNote: "Billed annually ($288/year)",
    features: [...],
    cta: { text: "Start Trial", href: "/signup" }
  }
]

// Display logic
const displayPrice = interval === 'monthly'
  ? tier.monthlyPrice
  : tier.annualPrice

Accessibility

The reference implementation includes:

  • Semantic HTML - Uses <section>, <h2>, <h3>, <ul>, <li>
  • ARIA Labels - Proper labeling for pricing cards
  • Keyboard Navigation - All CTAs keyboard accessible
  • Focus Management - Visible focus states
  • Screen Readers - Descriptive content for assistive tech
<section className="pricing" aria-labelledby="pricing-title">
  <h2 id="pricing-title">{title}</h2>

  <div className="pricing-grid" role="list">
    {tiers.map(tier => (
      <article
        className="pricing-tier"
        role="listitem"
        aria-label={`${tier.name} plan, ${tier.price}`}
      >
        <h3>{tier.name}</h3>
        <p className="price" aria-label={`Price: ${tier.price}`}>
          {tier.price}
        </p>

        <ul aria-label={`${tier.name} features`}>
          {tier.features.map(feature => (
            <li key={feature}>{feature}</li>
          ))}
        </ul>

        <a href={tier.cta.href} className="cta">
          {tier.cta.text}
        </a>
      </article>
    ))}
  </div>
</section>

Best Practices

Tier Naming

  • Keep it simple: "Starter", "Pro", "Enterprise"
  • Avoid clever names: Clear > cute
  • Be consistent: Don't mix styles
  • Show progression: Free → Pro → Enterprise

Price Display

  • Be clear: Include billing period
  • Use consistent format: Pick one style
  • Highlight savings: "Save 20%" for annual
  • Explain custom pricing: "Contact Sales" > "Custom"

Features

  • Be specific: "100GB storage" > "More storage"
  • Order by value: Most important first
  • Keep it scannable: 5-10 features per tier
  • Use plain language: Avoid jargon

Highlighted Tier

  • One per section: Don't highlight multiple
  • Make it obvious: Clear visual distinction
  • Label it: "Most Popular", "Best Value"
  • Place in center: On desktop layouts

CTAs

  • Vary by tier: Free = "Sign Up", Paid = "Start Trial"
  • Be specific: "Start 14-Day Trial" > "Get Started"
  • Use action verbs: "Start", "Try", "Contact"
  • Keep consistent: Same CTA text for similar tiers

Composition

In LandingPage

Pricing typically follows features:

<LandingPage>
  <Hero {...heroProps} />
  <Features {...featuresProps} />
  <Pricing {...pricingProps} />
  <Testimonials {...testimonialsProps} />
  <FAQ {...faqProps} />
  <CTA {...ctaProps} />
</LandingPage>

With FAQ

Place FAQ after pricing to address price objections:

<Pricing {...pricingProps} />
<FAQ
  title="Pricing FAQ"
  items={[
    {
      question: "Can I change plans anytime?",
      answer: "Yes, upgrade or downgrade at any time."
    },
    {
      question: "What payment methods do you accept?",
      answer: "We accept all major credit cards and PayPal."
    }
  ]}
/>

TypeScript

Full type definitions:

import { z } from 'zod'
import type { Component } from 'mdxui'
import { CTASchema } from 'mdxui/zod'

export const PricingTierSchema = z.object({
  name: z.string(),
  price: z.string(),
  description: z.string().optional(),
  features: z.array(z.string()),
  cta: CTASchema,
  highlighted: z.boolean().default(false),
})

export const PricingPropsSchema = z.object({
  title: z.string().optional(),
  description: z.string().optional(),
  tiers: z.array(PricingTierSchema),
  showToggle: z.boolean().default(false),
})

export type PricingTier = z.infer<typeof PricingTierSchema>
export type PricingProps = z.infer<typeof PricingPropsSchema>
export type Pricing = Component<PricingProps>

On this page