# Pricing 2

A shadcn/ui block from the [@diarmuradi](https://diarmuradi.com/?utm_source=shadcn.exchange) registry, listed on [shadcn.exchange](https://shadcn.exchange/r/diarmuradi/pricing-2).

- Page, with a live preview: https://shadcn.exchange/r/diarmuradi/pricing-2
- Screenshot: https://shadcn.exchange/shot/diarmuradi/pricing-2/light.png
- Registry item: https://diarmuradi.com/r/pricing-2.json
- Listed: 2026-10-13, source last changed 2026-10-13

## Install

```bash
npx shadcn@latest add https://shadcn.exchange/r/diarmuradi/pricing-2.json
```

Run it in a project that has shadcn/ui set up. It adds the files below and installs what they depend on.

## Dependencies

- npm packages: `lucide-react`, `motion`
- Registry items: `https://diarmuradi.com/r/styles/base-nova/badge.json`, `https://diarmuradi.com/r/styles/base-nova/fancy-button.json`, `https://diarmuradi.com/r/styles/base-nova/slider.json`

## Files

### `pricing.tsx`

```tsx
"use client"

import { useId, useState } from "react"
import {
  AnimatePresence,
  motion,
  useReducedMotion,
  type Variants,
} from "motion/react"

import { Badge } from "@/components/ui/badge"
import { Slider } from "@/components/ui/slider"
import { FancyButton } from "@/components/ui/fancy-button"
import { ArrowUpRightIcon, CheckIcon } from 'lucide-react'

const animVariant: Variants = {
  hidden: { opacity: 0, y: -20, scale: 1.15 },
  visible: (i: number) => ({
    opacity: 1,
    y: 0,
    scale: 1,
    transition: {
      delay: i * 0.04,
      type: "spring",
      damping: 16,
      stiffness: 380,
    },
  }),
  exit: {
    opacity: 0,
    y: -12,
    scale: 1.1,
    transition: { duration: 0.15, ease: "easeIn" },
  },
}

type PriceTier = {
  maxVolume: number
  price: number
}

const priceTiers: PriceTier[] = [
  { maxVolume: 1000, price: 29 },
  { maxVolume: 3000, price: 79 },
  { maxVolume: 5000, price: 149 },
  { maxVolume: 7500, price: 249 },
  { maxVolume: 10000, price: 399 },
]

const features = [
  "Unlimited team members",
  "Volume-based pricing",
  "Advanced automation workflows",
  "Real-time analytics dashboard",
  "Dedicated success manager",
]

export function Pricing() {
  const [emailVolume, setEmailVolume] = useState(5000)

  const price = (
    priceTiers.find((tier) => emailVolume <= tier.maxVolume) ??
    priceTiers[priceTiers.length - 1]!
  ).price

  return (
    <section aria-label="Pricing" className="mx-auto w-full max-w-5xl">
      <div className="flex flex-col items-center gap-8">
        <div className="flex flex-col items-center gap-4 text-center">
          <Badge variant="secondary" className="w-fit">
            Flexible Pricing
          </Badge>
          <h2 className="text-4xl font-bold tracking-tight text-foreground sm:text-5xl">
            Pay for what you use
          </h2>
          <p className="max-w-xl text-base text-muted-foreground">
            Simple, transparent pricing that scales with your email volume. No
            hidden fees, no surprises.
          </p>
        </div>

        <div className="w-full rounded-4xl bg-muted p-1">
          <div className="grid grid-cols-1 gap-2 lg:grid-cols-2">
            <VolumeCard
              emailVolume={emailVolume}
              setEmailVolume={setEmailVolume}
            />
            <PlanCard price={price} />
          </div>
        </div>
      </div>
    </section>
  )
}

function VolumeCard({
  emailVolume,
  setEmailVolume,
}: {
  emailVolume: number
  setEmailVolume: (value: number) => void
}) {
  const volumeLabelId = useId()
  const conversions = Math.round(emailVolume * 0.15)
  const revenue = Math.round(emailVolume * 0.15 * 45)

  return (
    <div className="flex flex-col gap-6 rounded-3xl bg-card p-8 shadow-elevated-lg">
      <Badge variant="secondary" className="w-fit gap-1.5">
        <div className="size-2 rounded-full bg-primary" />
        Usage-based pricing
      </Badge>

      <div className="flex flex-col gap-2">
        <h3
          id={volumeLabelId}
          className="text-2xl font-semibold text-foreground"
        >
          Monthly email volume?
        </h3>
        <p className="text-sm text-muted-foreground">
          Flexible pricing that grows with your subscriber base. Pay only for
          emails that deliver results.
        </p>
      </div>

      <div className="flex flex-col gap-3">
        <p className="text-sm font-medium text-foreground">
          {emailVolume.toLocaleString()} emails/month
        </p>
        <Slider
          aria-labelledby={volumeLabelId}
          value={[emailVolume]}
          onValueChange={(value) =>
            setEmailVolume(Array.isArray(value) ? (value[0] ?? 0) : value)
          }
          min={0}
          max={10000}
          step={100}
          className="cursor-grab active:cursor-grabbing **:data-[slot=slider-thumb]:h-6.5 **:data-[slot=slider-thumb]:w-3 **:data-[slot=slider-thumb]:border-3 **:data-[slot=slider-thumb]:border-card **:data-[slot=slider-thumb]:bg-primary **:data-[slot=slider-thumb]:ring-0 **:data-[slot=slider-thumb]:focus-within:ring-3 **:data-[slot=slider-track]:h-2 **:data-[slot=slider-track]:bg-input/60"
        />
        <div className="flex items-center justify-between text-xs text-muted-foreground">
          <span>0</span>
          <span>10,000</span>
        </div>
      </div>

      <div className="flex flex-col gap-3">
        <h3 className="text-lg font-semibold text-foreground">
          Expected results
        </h3>
        <p className="text-sm text-muted-foreground">
          With {emailVolume.toLocaleString()} emails per month, here&apos;s the
          typical performance our customers achieve.
        </p>
        <ul className="flex flex-col gap-3">
          <li className="flex items-start gap-3">
            <div className="mt-0.5 flex size-4 shrink-0 items-center justify-center rounded-full bg-primary">
              <CheckIcon  className="size-2.5 text-primary-foreground" />
            </div>
            <span className="text-sm text-muted-foreground">
              Est. conversions: {conversions.toLocaleString()}
            </span>
          </li>
          <li className="flex items-start gap-3">
            <div className="mt-0.5 flex size-4 shrink-0 items-center justify-center rounded-full bg-primary">
              <CheckIcon  className="size-2.5 text-primary-foreground" />
            </div>
            <span className="text-sm text-muted-foreground">
              Revenue impact: ${revenue.toLocaleString()}
            </span>
          </li>
        </ul>
      </div>
    </div>
  )
}

function PlanCard({ price }: { price: number }) {
  const shouldReduceMotion = useReducedMotion()
  const priceChars = String(price).split("")

  return (
    <div className="flex flex-col gap-6 p-8">
      <div className="flex flex-col gap-2">
        <h3 className="text-2xl font-semibold text-foreground">
          Professional plan
        </h3>
        <p className="text-sm leading-relaxed text-muted-foreground">
          For growing teams managing email campaigns, automation, and customer
          engagement at scale.
        </p>
      </div>

      <div className="flex items-baseline gap-1">
        <div className="flex text-6xl font-semibold tracking-tight text-foreground">
          <span>$</span>
          <AnimatePresence initial={false} mode="popLayout">
            {priceChars.map((char, idx) => (
              <motion.span
                key={`${price}-${idx}`}
                variants={animVariant}
                initial={shouldReduceMotion ? false : "hidden"}
                animate="visible"
                exit={shouldReduceMotion ? undefined : "exit"}
                custom={idx}
              >
                {char}
              </motion.span>
            ))}
          </AnimatePresence>
        </div>
        <span className="text-xl text-muted-foreground">/mo</span>
      </div>

      <ul className="flex flex-col gap-3">
        {features.map((feature) => (
          <li key={feature} className="flex items-start gap-3">
            <div className="mt-0.5 flex size-4 shrink-0 items-center justify-center rounded-full bg-primary">
              <CheckIcon  className="size-2.5 text-primary-foreground" />
            </div>
            <span className="text-sm text-muted-foreground">{feature}</span>
          </li>
        ))}
      </ul>

      <FancyButton size="lg" className="w-full">
        Start free trial
        <ArrowUpRightIcon  data-icon="inline-end" />
      </FancyButton>
    </div>
  )
}
```
