Insight Card
One personal insight from the user's own data, with a tiny visual.
≈ 2,349 tokens per use (tool 1,882 · guidance 328 · typical payload 139)How token costs are estimated
Example: Insight Card
Where did my clothing budget go this month?
Clothing this month
₪450 in total, most of it at Northwind Apparel.
| Item | Value | Share |
|---|---|---|
| Northwind Apparel | ₪180 | 40% |
| Juniper & Co | ₪110 | 24% |
| Fabrikam Shoes | ₪70 | 16% |
| Tailspin Denim | ₪50 | 11% |
| Other | ₪40 | 9% |
When to use
- You have one personal insight from the user's own data worth a glance: where their money went, how often they went somewhere, how close they are to a budget, a streak, how many steps are left before a milestone.
- A tiny visual makes the sentence land faster than numbers alone.
When not to use
- The user wants to explore or compare a series over time. Use a Chart.
- There are many records to read. Use a Data Table.
- The insight isn't backed by the user's data. Don't invent one.
How the agent should use it
- State one insight in one short sentence that adds to the visual, and wrap the key figures in
**double asterisks**. Don't repeat what the visual already shows. Nothing else is parsed. - Every number in the sentence must come from the visual's data. Sums and counts of it are fine; estimates are not.
- Pick the visual for the question:
bubblesfor a split of spend,dot-calendarfor visits in a month,progressfor a budget or goal,comparefor this period vs last,streakfor a daily habit,sequencefor logged steps toward a milestone (meals before a weigh-in, doses, sessions). - For a streak, pass
countwhen it began before the days shown (say, an 8-day streak with this week's 5 days). - Keep bubbles to about 6, and fold the long tail into one item with
other: true. - Set
localeto the user's locale, andweekStartto their week.
Visuals
type | Shows | Notes |
|---|---|---|
bubbles | Packed circles, area proportional to value | Labels sit inside when they fit (on up to two lines); the rest are listed in a legend below. Hover, focus or tap a bubble for its label and value |
dot-calendar | One dot per day under locale-aware weekday initials | Marked days are filled, today is ringed. Days with a label can be hovered and focused |
progress | What's left (or over), large, above a bar toward the target, with "spent of target" as one quiet line | label names the figure for screen readers; it isn't shown |
compare | 2–4 horizontal bars, current period first | The first bar is emphasized |
sequence | A rounded track: done steps as overlapping circles with an icon, then a small dot per remaining step | Icons come from a closed set (meal, salad, breakfast, snack, drink, workout, walk, sleep, pill, check, book, water), default check. remainingLabel ("2 to go") is shown after the dots when set |
streak | The streak length, large, in days, over a row of days under weekday initials | Done days are filled with a check, upcoming days are an empty ring, today is ringed. count defaults to the consecutive done days ending at the last done one |
Interaction
The action link and the share button send a result each time they're pressed. An action with kind: "open" is a round arrow button at the bottom right, named by its label (also shown as a tooltip); details and transactions are text links. The card never locks, because there's nothing to decide: it's an insight with a way into the data behind it. The action is a closed set, so the agent can't wire arbitrary behavior; the host maps each kind to a screen.
Accessibility
Each visual is a figure with a summary (aria-label), and its data is in a visually hidden table or list, or in visible text. Bubbles, and calendar days with a label, are buttons named with their label and value: hover, focus or tap shows the tooltip, and Escape hides it. Other dots aren't tab stops. The sequence is read as "6 of 8 done" followed by a list ("Monday breakfast done, …, 2 to go"). The streak is read as "Streak: 8 days" followed by a list ("Monday done, …, Friday not yet"), with today marked aria-current="date". The SVG, bars and circles are hidden from screen readers.
Installation
npx shadcn@latest add https://your-site.com/r/insight-card.json
Usage
import { tool } from "ai"
import { insightCardTool } from "@/components/genui/insight-card.schema"
const tools = {
[insightCardTool.name]: tool({
description: insightCardTool.description,
inputSchema: insightCardTool.inputSchema,
}),
}When the user presses the action link or share, the agent (or your host) receives:
{ action: "details" } // or "transactions", "open", "share"Render it directly, mapping results to your app's screens:
<InsightCard
{...part.input}
status={part.state === "input-streaming" ? "streaming" : "ready"}
onResult={(r) => (r.action === "share" ? shareInsight(part) : openScreen(r.action))}
/>With LangGraph
Push it from a graph node with ui.push({ name: "show_insight", props: { toolCallId, ...args } }, { message }), and render the thread's UI messages with the Renderer.
Restoring from history
There's no finished state to restore. Render the stored tool input again; the card stays live.
Schema
| Prop | Type | Notes |
|---|---|---|
title | string | Up to 60 characters |
insight | string | One or two sentences; **bold** key figures. Numbers come from the data |
timestamp | string? | ISO date-time, shown relative next to the title ("Yesterday", "3 days ago", then a date) |
visual | one of the visuals below | Data-bound |
action | { label, kind }? | kind: details, transactions or open (a round arrow button labelled label) |
share | boolean? | Shows a share button. Default false |
locale | string? | BCP 47, e.g. en-GB. Formats numbers, currency, weekday initials and the relative timestamp |
| Visual | Fields |
|---|---|
bubbles | items: { label, value, display?, other? }[] (1–12), format?, currency? |
dot-calendar | month (YYYY-MM), weekStart? (0 Sunday, 1 Monday), marked: { date, value?, label? }[], format?, currency? |
progress | value, target, label? (read by screen readers, not shown), format?, currency? |
compare | items: { label, value }[] (2–4), format?, currency? |
streak | count? (default: consecutive done days ending at the last done one), label? (default "Streak"; read by screen readers, not shown), days: { date, done }[] (2–31, oldest first; may end with upcoming days) |
sequence | items: { label, icon?, done }[] (1–12, in order), remainingLabel? |
format is number (default) or currency, with an ISO 4217 currency.
Host props: status (streaming or ready), onResult and className.