# Message Timing URL: /docs/guides/message-timing Display stream timing metadata like duration, tokens per second, and time to first token. Display stream performance metrics — duration, tokens per second, TTFT — on assistant messages. Reading Timing Data \[#reading-timing-data] Use `useMessageTiming()` inside a message component to access timing data: ```tsx import { useMessageTiming } from "@assistant-ui/react"; const MessageTimingDisplay: FC = () => { const timing = useMessageTiming(); if (!timing?.totalStreamTime) return null; const formatMs = (ms: number) => ms < 1000 ? `${Math.round(ms)}ms` : `${(ms / 1000).toFixed(1)}s`; return ( {formatMs(timing.totalStreamTime)} {timing.tokensPerSecond !== undefined && ` · ${timing.tokensPerSecond.toFixed(1)} tok/s`} ); }; ``` Place it inside `MessagePrimitive.Root`, typically near the action bar: ```tsx {8} const AssistantMessage: FC = () => { return ( ); }; ``` MessageTiming Fields \[#messagetiming-fields] | Field | Type | Description | | ----------------- | --------- | ---------------------------------- | | `streamStartTime` | `number` | Unix timestamp when stream started | | `firstTokenTime` | `number?` | Time to first text token (ms) | | `totalStreamTime` | `number?` | Total stream duration (ms) | | `tokenCount` | `number?` | Estimated or actual token count | | `tokensPerSecond` | `number?` | Throughput (tokens/sec) | | `totalChunks` | `number` | Total stream chunks received | | `toolCallCount` | `number` | Number of tool calls | Runtime Support \[#runtime-support] | Runtime | Supported | Notes | | ------------------------- | :-------: | -------------------------------------------- | | DataStream | Yes | Automatic via `AssistantMessageAccumulator` | | AI SDK (`useChatRuntime`) | Yes | Automatic via client-side tracking | | Local (`useLocalRuntime`) | Yes | Pass timing in `ChatModelRunResult.metadata` | | ExternalStore | Yes | Pass timing in `ThreadMessageLike.metadata` | | LangGraph | No | Not yet implemented | | AG-UI | No | Not yet implemented | DataStream \[#datastream] Timing is tracked automatically inside `AssistantMessageAccumulator`. No setup required. ```tsx import { useDataStreamRuntime } from "@assistant-ui/react-data-stream"; const runtime = useDataStreamRuntime({ api: "/api/chat" }); // useMessageTiming() works out of the box ``` AI SDK (useChatRuntime) \[#ai-sdk-usechatruntime] Timing is tracked automatically on the client side by observing streaming state transitions and content changes. Timing is finalized when each stream completes. `tokenCount` and `tokensPerSecond` are estimated from text length. ```tsx import { useChatRuntime } from "@assistant-ui/react-ai-sdk"; const runtime = useChatRuntime({ api: "/api/chat" }); // useMessageTiming() works out of the box ``` Local (useLocalRuntime) \[#local-uselocalruntime] Pass timing in the `metadata` field of your `ChatModelRunResult`: ```tsx import type { ChatModelAdapter } from "@assistant-ui/react"; const myAdapter: ChatModelAdapter = { async run({ messages, abortSignal }) { const startTime = Date.now(); const result = await callMyAPI(messages, abortSignal); const totalStreamTime = Date.now() - startTime; return { content: [{ type: "text", text: result.text }], metadata: { timing: { streamStartTime: startTime, totalStreamTime, tokenCount: result.usage?.completionTokens, tokensPerSecond: result.usage?.completionTokens ? result.usage.completionTokens / (totalStreamTime / 1000) : undefined, totalChunks: 1, toolCallCount: 0, }, }, }; }, }; ``` ExternalStore (useExternalStoreRuntime) \[#externalstore-useexternalstoreruntime] Pass timing in the `metadata.timing` field of your `ThreadMessageLike` messages: ```tsx import type { ThreadMessageLike } from "@assistant-ui/react"; const message: ThreadMessageLike = { role: "assistant", content: [{ type: "text", text: fullText }], metadata: { timing: { streamStartTime: startTime, firstTokenTime, totalStreamTime, tokenCount, tokensPerSecond, totalChunks: chunks, toolCallCount: 0, }, }, }; ``` API Reference \[#api-reference] useMessageTiming() \[#usemessagetiming] ```tsx const timing: MessageTiming | undefined = useMessageTiming(); ``` Returns timing metadata for the current assistant message, or `undefined` for non-assistant messages or when no timing data is available. Must be used inside a `MessagePrimitive.Root` context.