Choose a plan
Compare plans and pick one before starting.
Choose a plan
Every plan starts with a 14-day free trial. No card needed.
Plan
Growth includes
- Everything in Starter
- Payroll in 4 countries
- Performance reviews
- Slack and Google Calendar sync
For 48 people: £3,686.40 a year after the trial.
choose-a-plan.tsx
"use client";
import { useState } from "react";
import { LuBuilding, LuCheck, LuRocket, LuSprout } from "react-icons/lu";
import { Badge, Button, RadioCard, RadioGroup, Segment, SegmentedControl } from "@nexera-ui/react";
const PLANS = [
{
id: "starter",
name: "Starter",
icon: <LuSprout />,
monthly: 4,
summary: "For teams up to 25 getting leave and records out of spreadsheets.",
features: ["Employee directory", "Leave requests and calendar", "Document storage, 10 GB"],
},
{
id: "growth",
name: "Growth",
icon: <LuRocket />,
monthly: 8,
summary: "For growing companies that run payroll and reviews in one place.",
features: [
"Everything in Starter",
"Payroll in 4 countries",
"Performance reviews",
"Slack and Google Calendar sync",
],
},
{
id: "scale",
name: "Scale",
icon: <LuBuilding />,
monthly: 14,
summary: "For organisations with several entities and strict compliance needs.",
features: [
"Everything in Growth",
"Multiple legal entities",
"SSO and audit log",
"Dedicated success manager",
],
},
];
const money = new Intl.NumberFormat("en-GB", {
style: "currency",
currency: "GBP",
maximumFractionDigits: 2,
});
/** Choose a plan: compare plans, pick monthly or yearly billing, and start a trial. */
export default function ChooseAPlan() {
const [plan, setPlan] = useState("growth");
const [period, setPeriod] = useState("yearly");
const [started, setStarted] = useState(false);
const chosen = PLANS.find((item) => item.id === plan) ?? PLANS[1];
if (!chosen) return null;
const price = (monthly: number) => money.format(period === "yearly" ? monthly * 0.8 : monthly);
return (
<div className="@container min-h-[560px] bg-page px-4 py-8 @3xl:px-8">
<div className="mx-auto grid max-w-4xl gap-6">
<header className="flex flex-wrap items-end justify-between gap-4">
<div className="grid gap-1">
<h2 className="text-heading-h2 text-primary">Choose a plan</h2>
<p className="text-body-default text-secondary">
Every plan starts with a 14-day free trial. No card needed.
</p>
</div>
<SegmentedControl
aria-label="Billing period"
selectedKeys={[period]}
onSelectionChange={(keys) => {
const [key] = [...keys];
if (key !== undefined) setPeriod(String(key));
}}
>
<Segment id="monthly" label="Monthly" />
<Segment id="yearly" label="Yearly, save 20%" />
</SegmentedControl>
</header>
<div className="grid gap-6 @3xl:grid-cols-[minmax(0,1fr)_18rem] @3xl:items-start">
<RadioGroup type="card" label="Plan" name="plan" value={plan} onChange={setPlan}>
{PLANS.map((item) => (
<RadioCard
key={item.id}
value={item.id}
icon={item.icon}
title={`${item.name} · ${price(item.monthly)} per person a month`}
meta={
item.id === "growth" ? (
<Badge label="Most popular" tone="brand" variant="solid" dot={false} />
) : undefined
}
description={item.summary}
/>
))}
</RadioGroup>
<section
aria-labelledby="plan-summary"
className="grid gap-4 rounded-xl border border-default bg-surface p-5"
>
<h3 id="plan-summary" className="text-heading-h4 text-primary">
{chosen.name} includes
</h3>
<ul className="m-0 grid list-none gap-2 p-0">
{chosen.features.map((feature) => (
<li key={feature} className="flex items-start gap-2 text-body-default text-primary">
<LuCheck aria-hidden="true" className="mt-1 shrink-0 text-status-success-fg" />
{feature}
</li>
))}
</ul>
<p className="text-body-small text-secondary">
For 48 people:{" "}
{money.format(
(period === "yearly" ? chosen.monthly * 0.8 * 12 : chosen.monthly) * 48,
)}{" "}
{period === "yearly" ? "a year" : "a month"} after the trial.
</p>
<Button
size="md"
className="w-full"
onPress={() => {
setStarted(true);
}}
>
Start free trial
</Button>
<p role="status" className="text-body-small text-status-success-fg empty:hidden">
{started ? `Your ${chosen.name} trial is running until 21 October 2026.` : ""}
</p>
</section>
</div>
</div>
</div>
);
}