# Context breakdown
URL: /elements/context-breakdown

Where the window actually went: prompt, tools, files, conversation, and what's left.

> For AI agents: a documentation index is available at [llms.txt](/llms.txt). Use `.md` for canonical markdown pages; `.mdx` is kept as a backwards-compatible alias on supported URL paths.

A labeled bar and legend showing what is occupying the model's context window, with the leftover headroom computed rather than passed in. With a runtime two of those labels come from the thread's real per-turn token usage; standalone every segment, and its color, is yours to define.

## Getting started

**With a runtime:**

With a runtime, assistant-ui does not label context usage by category the way this element does. What a `ChatModelAdapter` can report is two real numbers per turn: input tokens and output tokens, carried on each assistant message's generation steps.

1. ### Derive the segments that are actually real

   A finer split, separating tool schema tokens from the rest of the prompt, or metering files apart from the running conversation, is not something the thread state tracks: providers report input as one combined figure. Stick to the two segments the runtime can tell you about, and add flat, application-known segments (a fixed system-prompt cost, for instance) alongside them only when you already track that cost yourself.

   ```
   "use client";

   import { useAuiState } from "@assistant-ui/react";
   import {
     ContextBreakdown,
     type ContextSegment,
   } from "@/components/assistant-ui/elements/context-breakdown";

   function useTurnUsage() {
     return useAuiState((s) =>
       s.thread.messages.reduce(
         (sum, message) => {
           for (const step of message.metadata.steps ?? []) {
             sum.input += step.usage?.inputTokens ?? 0;
             sum.output += step.usage?.outputTokens ?? 0;
           }
           return sum;
         },
         { input: 0, output: 0 },
       ),
     );
   }

   const MODEL_CONTEXT_WINDOW = 128_000;

   export function ThreadContextBreakdown() {
     const usage = useTurnUsage();
     const segments: ContextSegment[] = [
       { label: "Prompt", tokens: usage.input, tint: "bg-foreground/30" },
       { label: "Reply", tokens: usage.output, tint: "bg-blue-500/70" },
     ];
     return <ContextBreakdown segments={segments} limit={MODEL_CONTEXT_WINDOW} />;
   }
   ```

**Standalone (no runtime):**

Standalone, `ContextBreakdown` renders whatever segments you pass; it only sums their tokens against `limit` to compute the bar widths and the leftover headroom.

1. ### Pass the segments and the limit

   ```
   "use client";

   import { ContextBreakdown } from "@/components/assistant-ui/elements/context-breakdown";

   const segments = [
     { label: "System prompt", tokens: 1_400, tint: "bg-foreground/20" },
     { label: "Tools", tokens: 3_100, tint: "bg-foreground/35" },
     { label: "Conversation", tokens: 42_000, tint: "bg-blue-500/70" },
   ];

   export function ContextPanel() {
     return <ContextBreakdown segments={segments} limit={128_000} />;
   }
   ```

## Anatomy

```
<div data-slot="context-breakdown">
  <div>
    <span>Context</span>
    <span>{/* used / limit, amber past 85% */}</span>
  </div>
  <div>{/* stacked bar, one named meter per segment, widths from tokens / limit */}</div>
  <div>
    {/* one row per segment: dot, label, count */}
    <div>{/* Headroom row: fixed dot, limit minus used, clamped at 0 */}</div>
  </div>
</div>
```

Headroom is not a segment you pass; it is `limit` minus the sum of every segment's `tokens`, floored at zero, and it never gets a bar slice of its own, only a legend row. A zero `limit` is treated as zero pressure rather than dividing by it, so the header stays plain instead of showing a red or broken percentage. Every segment supplies its own `tint`, a Tailwind background class shared verbatim between the bar slice and the legend dot, so the two never disagree. Each painted segment is a named meter whose `0…100` value matches its share of the context limit and whose value text reads the same token count the legend prints. A segment that rounds to no announced width is left out of the bar entirely rather than announced as an empty one.

## Examples

### Choosing tints

Any Tailwind background utility works; segments read cleanly when their tints step through opacity or hue together, as in the runtime example above.

```
{ label: "Conversation", tokens: 42000, tint: "bg-blue-500/70" }
```

### Restyle the panel

```
<ContextBreakdown className="max-w-xs gap-2" segments={segments} limit={limit} />
```

## API reference

**With a runtime:**

### Thread state

| Selector            | Type                      | Description                                                                                                                                                                                                                |
| ------------------- | ------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `s.thread.messages` | `readonly MessageState[]` | Every message in the active thread. Each assistant message's `metadata.steps` is a `readonly ThreadStep[]`, and a step may carry `usage: { inputTokens: number; outputTokens: number }` when the model adapter reports it. |

There is no selector for the model's context window size or for a category split beyond input and output; both are supplied as shown above.

**Standalone (no runtime):**

### ContextBreakdown

| Prop        | Type                        | Default  | Description                                                                                                                                       |
| ----------- | --------------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------- |
| `segments`  | `readonly ContextSegment[]` | required | What is occupying the window, in stacking order. Headroom is derived, not passed.                                                                 |
| `limit`     | `number`                    | required | Window size. The header turns amber once the segments pass 85 percent of it. A zero limit is treated as zero pressure rather than dividing by it. |
| `className` | `string`                    |          | Merged onto the root.                                                                                                                             |

### ContextSegment

| Prop     | Type     | Default  | Description                                                                           |
| -------- | -------- | -------- | ------------------------------------------------------------------------------------- |
| `label`  | `string` | required | What this slice of the window is, shown in the legend.                                |
| `tokens` | `number` | required | Raw token count. The element formats it.                                              |
| `tint`   | `string` | required | Background class shared by the bar slice and the legend dot, so the two always agree. |

All other `div` props are forwarded to the root.