Staaarter

Billing Team Seats

A seat management block that lists team members with their role and invitation status, plus a seat stepper that adjusts the seats on the plan and recalculates the monthly total live. Built for workspace settings and per-seat billing portals where the seat count doesn't always match the number of active members.

By Staaarter Team
Billing
Updated May 3, 2026
Docs
Live preview

Docs

Installation

A real shadcn registry command, not a copy-paste stand-in: it fetches this block plus its avatar, badge, button dependencies and any missing npm packages, and installs them straight into your project.

npx shadcn add https://staaarter.com/r/billing-team-seats.json

Prefer not to use the CLI? Copy the source from the Code toggle above into src/components/blocks/billing/billing140.tsx instead.

Usage

The file also exports billing140Demo, the exact props behind the preview above. Spread it to get a working section in one line, then replace it with your own data.

import { Billing140, billing140Demo } from "@/components/blocks/billing/billing140";

export default function Page() {
  return <Billing140 {...billing140Demo} />;
}

Props

PropTypeDefaultDescription
headingstring"Team seats"Heading shown above the member list.
descriptionstringnoneOptional supporting text under the heading.
membersTeamMember[]noneMembers rendered in the order given, each with a name, email, role, and optional pending status.
pricePerSeatnumbernonePrice charged per seat per interval, used to compute the total.
currencySymbolstring"$"Symbol shown next to the per-seat price line.
intervalLabelstring"/mo"Suffix shown after the total, e.g. /mo or /yr.
minSeatsnumbermembers.lengthLowest seat count the stepper allows. Defaults to the number of members passed in.
maxSeatsnumber100Highest seat count the stepper allows.
defaultSeatCountnumbermembers.lengthSeat count shown on first render, clamped to at least minSeats.
classNamestringnoneExtra classes for the outer container.

Types

interface TeamMember {
  id: string;
  name: string;
  email: string;
  role: "Admin" | "Member";
  avatarSrc?: string;
  status?: "active" | "pending";
}

Behavior notes

  • Seat count is local state, starting at defaultSeatCount (or the member count if omitted), and clamped between minSeats and maxSeats by the stepper buttons.
  • The total recalculates as seatCount times pricePerSeat on every stepper click; there's no server round trip built in.
  • The member list itself is static, changing the seat count doesn't add or remove rows. Wire your own invite/remove flow separately and pass an updated members array.
  • If the seat count is set below the number of listed members (via minSeats), a warning line explains how many members won't fit.
  • The minus button disables at minSeats and the plus button disables at maxSeats so the count can't be pushed out of range.

Frequently asked questions