Insight Card

One personal insight from the user's own data, with a tiny visual.

Pattern

≈ 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.

ItemValueShare
Northwind Apparel₪18040%
Juniper & Co₪11024%
Fabrikam Shoes₪7016%
Tailspin Denim₪5011%
Other₪409%

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: bubbles for a split of spend, dot-calendar for visits in a month, progress for a budget or goal, compare for this period vs last, streak for a daily habit, sequence for logged steps toward a milestone (meals before a weigh-in, doses, sessions).
  • For a streak, pass count when 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 locale to the user's locale, and weekStart to their week.

Visuals

typeShowsNotes
bubblesPacked circles, area proportional to valueLabels 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-calendarOne dot per day under locale-aware weekday initialsMarked days are filled, today is ringed. Days with a label can be hovered and focused
progressWhat's left (or over), large, above a bar toward the target, with "spent of target" as one quiet linelabel names the figure for screen readers; it isn't shown
compare2–4 horizontal bars, current period firstThe first bar is emphasized
sequenceA rounded track: done steps as overlapping circles with an icon, then a small dot per remaining stepIcons 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
streakThe streak length, large, in days, over a row of days under weekday initialsDone 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

app/api/chat/route.ts
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

PropTypeNotes
titlestringUp to 60 characters
insightstringOne or two sentences; **bold** key figures. Numbers come from the data
timestampstring?ISO date-time, shown relative next to the title ("Yesterday", "3 days ago", then a date)
visualone of the visuals belowData-bound
action{ label, kind }?kind: details, transactions or open (a round arrow button labelled label)
shareboolean?Shows a share button. Default false
localestring?BCP 47, e.g. en-GB. Formats numbers, currency, weekday initials and the relative timestamp
VisualFields
bubblesitems: { label, value, display?, other? }[] (1–12), format?, currency?
dot-calendarmonth (YYYY-MM), weekStart? (0 Sunday, 1 Monday), marked: { date, value?, label? }[], format?, currency?
progressvalue, target, label? (read by screen readers, not shown), format?, currency?
compareitems: { label, value }[] (2–4), format?, currency?
streakcount? (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)
sequenceitems: { 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.

Source

On this page