Blocks

Pricing Usage Slider

A two-plan pricing section where one card prices itself from a volume slider.

A pricing section built for plans that scale with usage. One card carries a fixed price; the other prices itself from a slider — drag the handle and the figure spins with Number Flow while the track fills behind the handle.

New tab

Flexible pricing for every creator

Start free and scale as you grow. No contracts, no surprises — just work that ships.

Most popular

Creator

Best for solo creators publishing every week

$79 /mo

Cancel or pause any time

  • 600+ minutes monthly
  • One custom cloned voice
  • Commercial usage license

Scale

Pay for the volume your team actually uses

$750 /mo

Billed monthly, adjust any time

Drag to set your monthly volume

  • Custom SLA and volume
  • Unlimited cloned voices
  • SSO, SAML and a dedicated CSM

Installation

pnpm dlx shadcn@latest add @tween-ui/pricing-usage-slider

Usage

app/page.tsx
app/page.tsx
import PricingUsageSlider from '@/components/tweenui/pricing-usage-slider';
 
export default function Page() {
  return <PricingUsageSlider />;
}

A plan is slider-driven when it carries a range instead of a price:

app/page.tsx
app/page.tsx
import PricingUsageSlider from '@/components/tweenui/pricing-usage-slider';
 
export default function Page() {
  return (
    <PricingUsageSlider
      title="Flexible pricing for every creator"
      plans={[
        {
          name: 'Creator',
          description: 'Best for solo creators publishing weekly',
          price: 79,
          priceNote: 'Cancel or pause any time',
          popular: true,
          features: ['600+ minutes monthly', 'One cloned voice', 'Commercial license'],
          cta: 'Upgrade now',
          onClick: () => router.push('/signup'),
        },
        {
          name: 'Scale',
          description: 'Pay for the volume your team actually uses',
          range: { min: 399, max: 1200, defaultValue: 750, label: 'Drag to set your volume' },
          features: ['Custom SLA and volume', 'Unlimited cloned voices', 'SSO and SAML'],
          cta: 'Contact sales',
          onClick: () => router.push('/sales'),
        },
      ]}
    />
  );
}

Props

PropTypeDefaultDescription
titleReactNodesample headingSection heading.
descriptionstringsample lineSupporting line under the heading.
plansPricingUsageSliderPlan[]two plansPlans side by side. Two is the shape.
popularLabelstringMost popularBadge on the highlighted card.

Extends native <section> attributes (className, style, …).

PricingUsageSliderPlan

FieldTypeDescription
name · descriptionstringPlan identity.
pricenumberFixed monthly price. Omit it when using range.
priceNotestringSmall print under the price.
featuresstring[]Ticked list. Three keeps the two cards level.
ctastringButton label.
onClick() => voidRuns when that plan is chosen.
popularbooleanHighlights the card and shows the badge.
rangePricingUsageSliderRangeTurns the price into a slider.

PricingUsageSliderRange

FieldTypeDescription
min · maxnumberEnds of the track.
defaultValuenumberWhere the handle starts.
labelstringCaption under the slider, and its a11y name.

Both cards use Slide Arrow Button, so the CLI installs that item too.

The slider paints its own filled track through an inline gradient, so the component needs no stylesheet of its own — nothing to copy beyond the one file.

Accessibility

The slider is a native <input type="range">, so it arrows and pages from the keyboard for free, and range.label becomes its accessible name. The section has no entrance animation, so there is nothing for prefers-reduced-motion to switch off here.