Xzero UI
Pro library

AI insight cardPro

A KPI card that explains itself. Explain streams a short reason for the change and ranks three drivers with contribution bars. A numbered annotation lights up on the sparkline as each driver is read, and follow-up chips ask the next question in place.

$ pnpm dlx shadcn@4.23.0 add @xzero-ui-pro/ai-insight-card
Live · keyboard ready

Pass your KPI as metric: totals, the current and previous series, and point labels. Pass followUps for the chips. Wire explain({ metric, followUp, signal }) to your analysis endpoint. It should resolve with { segments, drivers }, where each driver has a signed delta and the series index its annotation marks. Write one text segment per driver, with driver set and in rank order, so the chart lights up as the reader reaches it. The signal aborts on Stop, Hide or a new question. If the promise rejects, the card shows an error row with Retry. Use lowerIsBetter for churn or cost metrics so a rise reads red, and format for metrics that are not currency. With explain set, autoPlay is off by default, so the card never calls the model on its own.

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/ai-insight-card

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 { AiInsightCard } from "@/components/xzero/blocks/ai-insight-card/ai-insight-card";

export function RevenueCard({ metric }) {
  return (
    <AiInsightCard
      metric={metric}
      followUps={[{ id: "plan", label: "Break down by plan" }]}
      explain={async ({ metric, followUp, signal }) => {
        const res = await fetch("/api/insights/explain", {
          method: "POST",
          body: JSON.stringify({ metric: metric.label, followUp: followUp?.id }),
          signal,
        });
        if (!res.ok) throw new Error("The analysis service did not respond.");
        return res.json(); // { segments: [{ text, driver? }], drivers: [{ id, label, delta, point }] }
      }}
    />
  );
}

API reference

AiInsightCard

A KPI card with a sparkline that explains its own change: Explain streams a short reason, ranks three drivers with contribution bars and lights up a numbered chart annotation as each driver is read. Follow-up chips ask the next question in place.

PropTypeDefaultDescription
metricInsightMetricsampleMetricThe KPI: label, period, value, previous, comparisonLabel, series, previousSeries, points (tooltip labels), legend labels, an optional format function and lowerIsBetter to flip delta colors.
insightInsightsampleInsightThe local answer for Explain: segments of text (a segment with driver lights up that driver) and up to three ranked drivers, each with a signed delta and the series index its annotation marks.
followUpsInsightFollowUp[]sampleFollowUpsFollow-up chips under the answer. Each has an id, a label and an optional local insight. The chip showing its answer reads as pressed.
explain(request: { metric, followUp?, signal }) => Promise<Insight>–Fetch a real answer. Resolve with the full insight; the card streams it in chunks and syncs the annotations. The signal aborts on Stop, Hide or a new question. A rejection shows the error row with Retry.
onExplain(followUp?: InsightFollowUp) => void–Called when Explain is pressed (no argument) or a follow-up is picked.
autoPlaybooleantrue without explainPlays Explain and each follow-up on a loop while the card is on screen, until the reader interacts. Off with reduced motion.
thinkingLabelstring"Reading invoices and renewals"Shimmering status line while the answer is prepared.
source{ label: string; icon?: ReactNode } | nullStripe billingData source in the footer. Pass null to hide it.
model{ label: string; icon?: ReactNode } | nullClaude Sonnet 5.5Model in the footer. Pass null to hide it.
classNamestring–Class on the card root.

Keyboard interactions

KeysAction
TabMoves through Explain (Stop while streaming, Hide when open), the chart, Retry and the follow-up chips.
Enter / SpaceExplains the change, stops a stream, hides the panel or asks a follow-up.
Left / RightWith the chart focused, steps through the points and announces each value.
Home / EndJumps to the first or last point.
EscapeClears the chart readout.

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