Skip to content
kerfjsGitHub

PlanPicker

Compare plans, see the price difference, and switch.

SourcePackage @kerf-js/veneer

Preview

tsx
import { state } from '@kerf-js/core'import { PlanPicker, Toaster } from '@kerf-js/veneer'import type { BillingPlan } from '@kerf-js/veneer'export function Team() {  const current = state('team')  const change = async (plan: { id: string }) => {    await wait(700)    current.value = plan.id  }  return (    <>      <PlanPicker        plans={PLANS}        current={current.value}        seats={8}        daysLeft={9}        losses={(plan) =>          plan.id === 'hobby'            ? [                'Preview environments',                '7 of your 10 seats',              ]            : []        }        onChange={change}      />      <Toaster />    </>  )}const PLANS: BillingPlan[] = [  {    id: 'hobby',    name: 'Hobby',    price: 0,    description: 'For side projects.',    features: [      '3 seats',      '100 deploys a month',      '1 region',    ],  },  {    id: 'team',    name: 'Team',    price: 24,    description: 'For product teams.',    features: [      '10 seats',      'Unlimited deploys',      '5 regions',      'Preview environments',    ],  },  {    id: 'scale',    name: 'Scale',    price: 64,    description: 'For many teams.',    features: [      '50 seats',      'Unlimited deploys',      'Every region',      'SSO and audit export',    ],  },]const wait = (ms: number) =>  new Promise<void>((done) => {    setTimeout(done, ms)  })

Installation

npm install @kerf-js/veneer

Usage

tsx
import { PlanPicker } from '@kerf-js/veneer'

Accessibility

  • This has no shortcuts of its own: controls inside it use Tab, Enter and Space as usual, and veneer's parts keep their roles and labels.

API Reference

PropTypeDefaultDescription
plansBillingPlan[]—The plans to compare, one card each.
currentstring—The id of the plan the workspace is on.
selectedState<string>—The checked card, when something else needs to set it.
intervalState<BillingInterval>—Monthly or yearly, when you want to own it.
seatsnumber—Seats, for the price.
daysLeftnumber—Days left in this period, for the proration.
periodDaysnumber—The length of a period in days.
currencystring—The ISO 4217 code prices are shown in, such as EUR.
localestring—The language and region used to format values.
preview((plan: BillingPlan, interval: BillingInterval) => Proration | Promise<P…—Your server's proration, to replace the built-in one.
losses((plan: BillingPlan) => string[])—What stops working on a downgrade.
EventTypeDefaultDescription
onChange(plan: BillingPlan, interval: BillingInterval) => string | void | Promis…—Makes the change. Resolve with a message to keep the dialog open and show it.

RelatedUsage meterInvoicesPayment methods