Xzero UI
Pro library

Account healthPro

A customer success card where a health score gauge, a renewal countdown and five sourced signals with sparklines lead into an AI risk summary and one recommended next step, like scheduling a QBR before usage slips further.

$ pnpm dlx shadcn@4.23.0 add @xzero-ui-pro/account-health
Live · keyboard ready

Drop it into an account or renewal page and pass account (name, plan, ARR, logo, term start and renewal date) and periods (one per time range, each with a score, the previous score, signals, a summary and an action). Pass summarize(input, signal) to return the summary text from your model. The card handles the thinking, streaming, abort and error states. Pass onAction(action, input, signal) to book the QBR for real, and reject it to show the retry state. period/onPeriodChange control the time range, and asOf fixes the countdown date. Keep the same number of trend points in every period so the sparklines morph from one to the next. With no props it plays a sample Linear Enterprise account.

When to use

When not to use

Installation

Register both Xzero UI namespaces in your project's components.json:

{
  "registries": {
    "@xzero-ui": "https://mcp.xzero.co.uk/r/{name}.json",
    "@xzero-ui-pro": "https://mcp.xzero.co.uk/r/pro/{name}.json"
  }
}
npx shadcn@4.23.0 add @xzero-ui-pro/account-health

Install xzero-foundation once, and import @/components/xzero/foundation.css in your app root. No Xzero token is used by this snapshot. For sample portraits, photos, fonts, or logos, download https://mcp.xzero.co.uk/install-assets.mjs and run node xzero-install-assets.mjs <receiving-project>.

Usage

import { AccountHealth } from "@/components/xzero/blocks/account-health/account-health";

export function CustomerPanel({ account, periods }) {
  return (
    <AccountHealth
      account={account}
      periods={periods}
      summarize={async ({ account, period }, signal) => {
        const res = await fetch("/api/health-summary", { method: "POST", body: JSON.stringify({ id: account.name, period: period.id }), signal });
        return (await res.json()).text;
      }}
      onAction={async (action, { account }, signal) => {
        await fetch("/api/qbr", { method: "POST", body: JSON.stringify({ account: account.name }), signal });
      }}
    />
  );
}

API reference

AccountHealth

A customer success card: customer and plan, a health score gauge with trend, a renewal countdown, signal rows with sparklines and status, and an AI risk summary that ends in one recommended next step. With no props it plays a sample Linear Enterprise account.

PropTypeDefaultDescription
accountAccountHealthAccountLinear sampleThe customer: name, plan, arr (formatted string), logo (ReactNode, about 24px), termStart and renewalDate (Date or ISO date).
periodsAccountHealthPeriod[]30d and 90d samplesOne entry per time range: id, label, spanLabel, score, previousScore, signals, summary and action. Keep the same number of trend points across periods so sparklines morph.
periodstring–Controls the selected period id. Pair with onPeriodChange.
defaultPeriodstringfirst periodThe period selected on mount when uncontrolled.
onPeriodChange(period: string) => void–Called when someone picks another time range.
summarize(input: { account, period }, signal: AbortSignal) => Promise<string>–Writes the risk summary. Return the full text and the card streams it in. Aborted when the period changes or the summary is regenerated. Leave it out to use each period's summary, simulated.
onAction(action, input: { account, period }, signal: AbortSignal) => Promise<void> | void–Runs the recommended action, such as booking a QBR. Reject to show the error state with a retry. Leave it out to simulate it.
model{ name: string; mark?: ReactNode }{ name: "Claude Sonnet 5.5" }The model credited on the summary, with its 14px mark. Defaults to the Claude mark.
asOfDate | stringtodayThe date the renewal countdown counts from. The sample account uses Oct 27, 2026.
classNamestring–Extra class on the card.

AccountHealthSignal

One signal row.

PropTypeDefaultDescription
id (required)string–Stable key, shared across periods.
label (required)string–"Product usage", "Seat utilization", "Support tickets", "NPS", "Invoice status".
detailstring–One short line of evidence: "Analytics down 18%".
value (required)string–The headline value, right aligned in tabular numerals.
status (required)"good" | "watch" | "risk"–Shown as an icon plus Good, Watch or At risk, and colors the sparkline when not good.
trendnumber[]–Sparkline points, oldest first.
sourceReactNode–The source's 14px mark, such as PostHog, Okta, Zendesk, Intercom or Stripe.
sourceLabelstring–Names the source for screen readers: "From Zendesk".

AccountHealthAction

The recommended next step under the summary.

PropTypeDefaultDescription
label (required)string–Button label: "Schedule QBR".
title (required)string–The recommendation as a sentence: "Schedule a QBR with Linear".
reason (required)string–Why, in a few words: "Usage dropped 18% in Analytics".
doneLabelstring–Shown once the action has run: "Invite drafted for Tue, Nov 3, 10:00".

Keyboard interactions

KeysAction
TabMoves through the time range, regenerate and action buttons.
ArrowLeft / ArrowRightSwitches the time range inside the segmented control.
Enter / SpaceRegenerates the summary or runs the recommended action.

Accessibility

Motion

Responsive behavior

Performance

Notes for AI

Guidance for AI tools

Blocks are complete, self-contained screens with sample data. Replace the sample data and connect the callbacks described above. Follow the declared prop types and do not invent props. Keep keyboard access, reduced motion support, and both light and dark themes intact when adapting it.

Full library index: https://ui.xzero.co.uk/llms.txt

Browse all components