Editorial

Pricing Toggle

Billing period switch, such as monthly and yearly, built on the stock Tabs list. A controlled client component that takes a typed value and a change handler.

Free, MIT licenseOpen in Browse
"use client"

import type { ComponentProps } from "react"

import { Tabs, TabsList, TabsTrigger } from "@/components/ui/tabs"

/**
 * The switch between billing periods, such as monthly and annual, above a
 * set of pricing plans. Put a `PricingToggleItem` inside for each period. It
 * is a controlled tab list: keep the chosen period in state, pass it as
 * `value` and update it in `onValueChange`.
 *
 * `className`, `aria-label` and other props land on the tab list. The toggle
 * centers itself (`self-center`) in a flex or grid parent.
 */
function PricingToggle<Value extends string>({
  value,
  onValueChange,
  ...props
}: Omit<ComponentProps<typeof TabsList>, "variant"> & {
  /**
   * The stock `TabsList` variant: `default` (the default) sets the periods
   * on a muted pill track, `line` drops the track and underlines the chosen
   * period.
   */
  variant?: ComponentProps<typeof TabsList>["variant"]
  /** The selected period. It matches one `PricingToggleItem`'s `value`. */
  value: Value
  /** Called with the `value` of the period the visitor picks. */
  onValueChange: (value: Value) => void
}) {
  return (
    <Tabs
      value={value}
      onValueChange={(next) => onValueChange(next as Value)}
      className="self-center"
    >
      <TabsList data-slot="pricing-toggle" {...props} />
    </Tabs>
  )
}

/**
 * One billing period inside `PricingToggle`, such as "Monthly". Give it a
 * `value`; `PricingToggle` reports that value when it is picked.
 */
function PricingToggleItem(props: ComponentProps<typeof TabsTrigger>) {
  return <TabsTrigger data-slot="pricing-toggle-item" {...props} />
}

export { PricingToggle, PricingToggleItem }

Installation

pnpm dlx shadcn add @01orbital/pricing-toggle
npx shadcn add @01orbital/pricing-toggle
yarn shadcn add @01orbital/pricing-toggle
bunx --bun shadcn add @01orbital/pricing-toggle

Free under the MIT license. Add the @01orbital registry to components.json once (install guide), then run the command.

It lands at components/editorial/pricing-toggle.tsx, as source you own and edit.

Usage

"use client"

import { useState } from "react"

import {
  PricingToggle,
  PricingToggleItem,
} from "@/components/editorial/pricing-toggle"

const COPY = {
  label: "Billing period",
  monthly: "Monthly",
  annual: "Yearly",
} as const

export default function PricingToggleDemo() {
  const [period, setPeriod] = useState<"monthly" | "annual">("monthly")
  return (
    <PricingToggle
      aria-label={COPY.label}
      value={period}
      onValueChange={setPeriod}
    >
      <PricingToggleItem value="monthly">{COPY.monthly}</PricingToggleItem>
      <PricingToggleItem value="annual">{COPY.annual}</PricingToggleItem>
    </PricingToggle>
  )
}

Anatomy

Pricing Toggle is built from 2 parts. In the example they nest like this:

PricingToggle
└─ PricingToggleItem
  • PricingToggle: The switch between billing periods, such as monthly and annual, above a set of pricing plans. Put a PricingToggleItem inside for each period. It is a controlled tab list: keep the chosen period in state, pass it as value and update it in onValueChange.
  • PricingToggleItem: One billing period inside PricingToggle, such as "Monthly". Give it a value; PricingToggle reports that value when it is picked.

API reference

PricingToggle

The switch between billing periods, such as monthly and annual, above a set of pricing plans. Put a PricingToggleItem inside for each period. It is a controlled tab list: keep the chosen period in state, pass it as value and update it in onValueChange.

className, aria-label and other props land on the tab list. The toggle centers itself (self-center) in a flex or grid parent.

Prop

Type

It also accepts the props of the Base UI part it wraps, and the HTML attributes of the element it renders.

PricingToggleItem

One billing period inside PricingToggle, such as "Monthly". Give it a value; PricingToggle reports that value when it is picked.

It accepts the props of the Base UI part it wraps, and the HTML attributes of the element it renders.

Dependencies

Registry pieces it installs with it:

  • tabs