# Context

Shows how much of the model's context window the conversation has used, with a breakdown on hover.

Source: https://suiss.app/ui/docs/ai/context

**React**

**Example**

```tsx
import { Button } from "@/components/ui/button"
import {
  HoverCard,
  HoverCardContent,
  HoverCardTrigger,
} from "@/components/ui/hover-card"
import { Progress } from "@/components/ui/progress"

const usage = {
  input: 8420,
  output: 1260,
  reasoning: 980,
  limit: 128_000,
}

const used = usage.input + usage.output + usage.reasoning
const percent = Math.round((used / usage.limit) * 100)

const TRAILING_ZERO = /\.0$/

function formatTokens(value: number) {
  if (value < 1000) {
    return String(value)
  }

  return `${(value / 1000).toFixed(1).replace(TRAILING_ZERO, "")}k`
}

const rows = [
  { label: "Input", value: usage.input },
  { label: "Output", value: usage.output },
  { label: "Reasoning", value: usage.reasoning },
]

export default function ContextDemo() {
  return (
    <div className="flex justify-center py-12">
      <HoverCard>
        <HoverCardTrigger
          delay={10}
          closeDelay={100}
          render={<Button variant="outline" size="sm" />}
        >
          {percent}% context
        </HoverCardTrigger>
        <HoverCardContent className="flex w-64 flex-col gap-3">
          <div className="flex items-center justify-between gap-3 text-sm">
            <span className="font-medium">Context</span>
            <span className="text-muted-foreground">
              {formatTokens(used)} / {formatTokens(usage.limit)}
            </span>
          </div>
          <Progress value={percent} aria-label="Context used" />
          <div className="flex flex-col gap-1 text-sm">
            {rows.map((row) => (
              <div key={row.label} className="flex justify-between gap-3">
                <span className="text-muted-foreground">{row.label}</span>
                <span>{formatTokens(row.value)}</span>
              </div>
            ))}
          </div>
        </HoverCardContent>
      </HoverCard>
    </div>
  )
}
```

Context shows how much of the model's context window the conversation has used. The trigger shows the share used, and a [Hover Card](https://suiss.app/ui/docs/components/hover-card) opens a [Progress](https://suiss.app/ui/docs/components/progress) bar with the input, output and reasoning tokens.

## Installation

Context is built with the [Hover Card](https://suiss.app/ui/docs/components/hover-card), [Progress](https://suiss.app/ui/docs/components/progress) and [Button](https://suiss.app/ui/docs/components/button) components.

```bash title="Terminal"
npx shadcn@latest add @esuiss/hover-card @esuiss/progress @esuiss/button
```

Or use the [suiss/ui CLI](https://suiss.app/ui/docs/cli):

```bash title="Terminal"
npx @esuiss/ui@latest add hover-card progress button
```

## Usage

```tsx
<HoverCard>
  <HoverCardTrigger
    delay={10}
    closeDelay={100}
    render={<Button variant="outline" size="sm" />}
  >
    {percent}% context
  </HoverCardTrigger>
  <HoverCardContent className="flex w-64 flex-col gap-3">
    <div className="flex items-center justify-between gap-3 text-sm">
      <span className="font-medium">Context</span>
      <span className="text-muted-foreground">
        {used} / {limit}
      </span>
    </div>
    <Progress value={percent} aria-label="Context used" />
  </HoverCardContent>
</HoverCard>
```

## AI SDK

Read usage from the metadata on the agent's message. `messageMetadata` runs when the stream finishes.

```ts title="app/api/chat/route.ts"
return result.toUIMessageStreamResponse({
  messageMetadata: ({ part }) => {
    if (part.type === "finish") {
      return { usage: part.totalUsage }
    }
  },
})
```

```tsx
const usage = message.metadata?.usage
const used = (usage?.inputTokens ?? 0) + (usage?.outputTokens ?? 0)
const percent = Math.round((used / limit) * 100)
```

`limit` is the model's context window. The AI SDK does not send it, so set it from the model you use.

## API Reference

See [Hover Card](https://suiss.app/ui/docs/components/hover-card#api-reference) and [Progress](https://suiss.app/ui/docs/components/progress#api-reference).

## Accessibility

- The trigger is a button, so the breakdown opens on focus as well as on hover.
- Give the progress bar an accessible name with `aria-label`, such as "Context used".
- Put the share in the trigger text, so it is announced without opening the card.

**React Native, SwiftUI, Jetpack Compose**

```tsx
import { View } from "react-native"
import { Button } from "@/components/ui/button"
import { Popover, PopoverContent, PopoverDescription, PopoverHeader, PopoverTitle, PopoverTrigger } from "@/components/ui/popover"
import { Progress } from "@/components/ui/progress"
import { Text } from "@/components/ui/text"

const usage = [
  { label: "Input", value: "11,200" },
  { label: "Output", value: "3,900" },
  { label: "Reasoning", value: "1,300" },
]

export function AiContextExample() {
  return (
    <View className="items-start">
      <Popover>
        <PopoverTrigger render={<Button variant="outline" size="sm" />}>
          <Text>8% context</Text>
        </PopoverTrigger>
        <PopoverContent align="start" className="w-72">
          <View className="gap-4">
            <PopoverHeader>
              <PopoverTitle>16,400 of 200,000 tokens</PopoverTitle>
              <PopoverDescription>Claude Opus 5.5</PopoverDescription>
            </PopoverHeader>
            <Progress value={8} />
            <View className="gap-1.5">
              {usage.map((row) => (
                <View key={row.label} className="flex-row justify-between">
                  <Text className="text-sm text-muted-foreground">
                    {row.label}
                  </Text>
                  <Text className="text-sm tabular-nums">{row.value}</Text>
                </View>
              ))}
            </View>
          </View>
        </PopoverContent>
      </Popover>
    </View>
  )
}
```

```swift
import EsuissUI
import SwiftUI

struct ContextExample: View {
  @State private var open = false

  var body: some View {
    Button("8% context") { open = true }
      .buttonStyle(.variant(.outline, size: .sm))
      .popover(isPresented: $open) {
        PopoverContent(width: 288) {
          PopoverHeader {
            PopoverTitle("16,400 of 200,000 tokens")
            PopoverDescription("Claude Opus 5.5")
          }
          ProgressView(value: 8, total: 100)
          VStack(spacing: 6) {
            ContextRow(label: "Input", value: "11,200")
            ContextRow(label: "Output", value: "3,900")
            ContextRow(label: "Reasoning", value: "1,300")
          }
        }
      }
  }
}

struct ContextRow: View {
  let label: String
  let value: String

  var body: some View {
    HStack {
      Text(verbatim: label).foregroundStyle(EsuissColor.mutedForeground)
      Spacer()
      Text(verbatim: value).monospacedDigit().foregroundStyle(EsuissColor.foreground)
    }
    .textStyle(.sm)
  }
}
```

```kotlin
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.width
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import com.esuiss.ui.components.Button
import com.esuiss.ui.components.ButtonSize
import com.esuiss.ui.components.ButtonVariant
import com.esuiss.ui.components.Popover
import com.esuiss.ui.components.PopoverDescription
import com.esuiss.ui.components.PopoverHeader
import com.esuiss.ui.components.PopoverTitle
import com.esuiss.ui.components.PopupAlign
import com.esuiss.ui.components.Progress
import com.esuiss.ui.components.Text
import com.esuiss.ui.theme.EsuissTheme

@Composable
fun ContextAIDefault() {
    var open by rememberSaveable { mutableStateOf(false) }
    Column {
        Popover(
            open = open,
            onOpenChange = { open = it },
            trigger = { Button("8% context", onClick = { open = true }, variant = ButtonVariant.Outline, size = ButtonSize.Sm) },
            modifier = Modifier.width(288.dp),
            align = PopupAlign.Start,
        ) {
            Column(verticalArrangement = Arrangement.spacedBy(16.dp)) {
                PopoverHeader {
                    PopoverTitle("16,400 of 200,000 tokens")
                    PopoverDescription("Claude Opus 5.5")
                }
                Progress(8f, Modifier.fillMaxWidth())
                Column(verticalArrangement = Arrangement.spacedBy(6.dp)) {
                    UsageRow("Input", "11,200")
                    UsageRow("Output", "3,900")
                    UsageRow("Reasoning", "1,300")
                }
            }
        }
    }
}

@Composable
fun UsageRow(label: String, value: String) {
    Row(Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween) {
        Text(label, color = EsuissTheme.colors.mutedForeground)
        Text(value)
    }
}
```

Context on mobile is built from the same parts as on the web: [Popover](https://suiss.app/ui/docs/components/popover), [Progress](https://suiss.app/ui/docs/components/progress), [Button](https://suiss.app/ui/docs/components/button). The example above is a static demo; connect it to your agent by rendering the same parts from your messages.

## Installation

**React Native**

```bash title="Terminal"
npx @esuiss/ui@latest add popover progress button
```

**SwiftUI**

```bash title="Terminal"
npx @esuiss/ui@latest add popover progress button --platform swift
```

**Jetpack Compose**

```bash title="Terminal"
npx @esuiss/ui@latest add popover progress button --platform kotlin
```

## API Reference

See [Popover](https://suiss.app/ui/docs/components/popover), [Progress](https://suiss.app/ui/docs/components/progress), [Button](https://suiss.app/ui/docs/components/button).

