Skip to Content
ComponentsPricing

Pricing

The Pricing component provides you with pricing tables and plans.

Live Examples

Live Example
☀️
Copy
View Code
import { Pricing } from '@jarllyng/nostromo' export default function PricingExample() { return ( <Pricing title="Choose Your Plan" subtitle="Flexible pricing for teams of all sizes" plans={[ {...
Live Example
☀️
Copy
View Code
import { Pricing } from '@jarllyng/nostromo' export default function PricingComparisonExample() { return ( <Pricing title="Compare Plans" variant="muted" columns={3} plans={[ {...
Live Example
☀️
Copy
View Code
import { Pricing } from '@jarllyng/nostromo' export default function PricingSimpleExample() { return ( <Pricing title="Simple Pricing" columns={2} plans={[ { id: 'monthly',...
Live Example
☀️
Copy
View Code
import { Pricing } from '@jarllyng/nostromo' export default function PricingBillingExample() { const [yearly, setYearly] = React.useState(false) return ( <Pricing title="Monthly or yearly" showYearly={yearly}...

Import

import { Pricing } from "@jarllyng/nostromo"; // or import { Pricing } from "@jarllyng/nostromo/pricing";

Usage

import { Pricing } from "@jarllyng/nostromo"; export default function Example() { return ( <Pricing title="Choose Your Plan" plans={[ { id: "starter", name: "Starter", price: { monthly: 9 }, period: "month", features: [ { id: "projects", name: "Up to 5 projects", included: true }, { id: "sso", name: "SSO", included: false }, ], cta: { text: "Get Started", href: "/signup" }, }, ]} /> ); }

Monthly and yearly

The switch appears as soon as you pass onToggleBilling, and showYearly says which way it is set. The component does not hold that state itself, so the period shown is always the one you asked for.

A plan is billed yearly only if it has a yearly price. One without keeps its monthly amount and its monthly period, rather than presenting a monthly figure under a /year label.

import React from "react"; import { Pricing } from "@jarllyng/nostromo"; export default function Example() { const [yearly, setYearly] = React.useState(false); return ( <Pricing showYearly={yearly} onToggleBilling={setYearly} plans={[ { id: "pro", name: "Pro", price: { monthly: 29, yearly: 290 }, features: [{ id: "sso", name: "SSO", included: true }], cta: { text: "Start free trial", href: "/signup" }, }, ]} /> ); }

The saving beside the switch is worked out from the prices themselves: 290 against 29 a month is 17% off, so that is what it says. When the plans save different amounts it reports the best one as “up to”, and when nothing is saved it says nothing at all. showYearlyDiscount={false} removes it, and yearlyDiscountLabel rewords it:

<Pricing showYearly={yearly} onToggleBilling={setYearly} billingToggleLabel="Skift til årlig betaling" yearlyDiscountLabel={(percent, exact) => exact ? `(Spar ${percent}%)` : `(Spar op til ${percent}%)` } plans={plans} />

Calls to action

cta.href renders a link and cta.onClick renders a button, so the control matches what it does. A plan that gives both navigates and calls the handler, which is the shape analytics usually wants.

{ text: "Get Started", href: "/signup" } // <a href="/signup"> { text: "Contact Sales", onClick: openSalesModal } // <button type="button"> { text: "Start trial", href: "/signup", onClick: track } // both

Props

PropTypeDefaultRequiredDescription
plansPricingPlan[]-YesThe plans to show (see the interface below)
titlestringundefinedNoSection title
subtitlestringundefinedNoSection subtitle
columns1 | 2 | 3 | 43NoColumns in the grid
variant"default" | "muted" | "accent""default"NoSection background
showYearlybooleanfalseNoWhether yearly prices are the ones shown
onToggleBilling(yearly: boolean) => voidundefinedNoPass it to render the switch; it reports the flip
billingToggleLabelstring"Bill yearly"NoAccessible name for that switch
showYearlyDiscountbooleantrueNoShow what the yearly prices save
yearlyDiscountLabel(percent: number, exact: boolean) => string(percent, exact) => ...NoWording of the saving
classNamestringundefinedNoAdditional CSS classes

PricingPlan

interface PricingPlan { id: string; name: string; description?: string; /** `yearly` is optional; a plan without one is never billed yearly. */ price: { monthly: number; yearly?: number }; currency?: string; /** * Overrides the period label, which is otherwise "month" or "year". * It is used for both, so leave it off if the plan can be billed yearly. */ period?: string; features: PricingFeature[]; /** `href` renders a link, `onClick` a button, both render a link that calls it. */ cta: { text: string; href?: string; onClick?: () => void }; badge?: { text: string; variant?: "default" | "accent" | "success" | "warning"; }; popular?: boolean; } interface PricingFeature { id: string; name: string; /** A feature the plan does not have is struck through rather than dropped. */ included: boolean; }
Last updated on