Elements

Number ticker

Digits that roll into place as a count updates in real time.

01234567890123456789,012345678901234567890123456789tokens generated
fig. 01 · plays once, replay from the corner

Installation

npx shadcn@latest add "@assistant-ui/elements-number-ticker"
First time? Set up a runtime

Runtime components read their state from an assistant-ui runtime. Add one to an existing project:

npx assistant-ui@latest init

Then wrap your app in a runtime provider:

import { AssistantRuntimeProvider } from "@assistant-ui/react";
import { useChatRuntime, AssistantChatTransport } from "@assistant-ui/ai-sdk";

export default function App() {
  const runtime = useChatRuntime({
    transport: new AssistantChatTransport({ api: "/api/chat" }),
  });

  return (
    <AssistantRuntimeProvider runtime={runtime}>
      {/* your components */}
    </AssistantRuntimeProvider>
  );
}

The installation guide covers new projects, templates, and API routes.

A number ticker rolls each digit into place on its own short transition instead of swapping the text in place, so a changing count reads as motion rather than a flicker. With a runtime the value comes from a tool's result; standalone you pass it in directly.

Getting started

With a runtime, the ticker only needs a number and a label, so its renderer is one of the smallest in the catalog: read result from the tool call, and fall back to zero while it is still running.

Render the tool call

components/assistant-ui/elements/token-count-tool-ui.tsx
"use client";

import type { ToolCallMessagePartComponent } from "@assistant-ui/react";
import { NumberTicker } from "@/components/assistant-ui/elements/number-ticker";

export const TokenCountToolUI: ToolCallMessagePartComponent<
  Record<string, never>,
  { total: number }
> = ({ result, status }) => (
  <NumberTicker
    value={status.type === "complete" ? (result?.total ?? 0) : 0}
    label="tokens generated"
  />
);

Register the tool

app/toolkit.ts
import { defineToolkit } from "@assistant-ui/react";
import { z } from "zod";
import { TokenCountToolUI } from "@/components/assistant-ui/elements/token-count-tool-ui";

export const toolkit = defineToolkit({
  get_token_count: {
    type: "frontend",
    description: "Get the number of tokens generated so far this session.",
    parameters: z.object({}),
    execute: async () => ({ total: currentTokenCount() }),
    render: TokenCountToolUI,
  },
});
app/MyRuntimeProvider.tsx
import { AssistantRuntimeProvider, AuiConfig, Tools } from "@assistant-ui/react";
import { useChatRuntime } from "@assistant-ui/ai-sdk";
import { toolkit } from "./toolkit";

export function MyRuntimeProvider({ children }: { children: React.ReactNode }) {
  const runtime = useChatRuntime();
  const config = AuiConfig({ tools: Tools({ toolkit }) });
  return (
    <AssistantRuntimeProvider runtime={runtime} config={config}>
      {children}
    </AssistantRuntimeProvider>
  );
}

See Tool UI for backend-defined tools and approval gates.

Examples

Formatting large counts

value is formatted with toLocaleString("en-US") before it splits into digits, so thousands separators appear as static characters between the rolling digits rather than digits themselves. Screen readers read the same formatted string from an aria-label on the row, instead of parsing the individual rolling spans.

<NumberTicker value={1234567} label="tokens generated" />
// renders "1,234,567", with "," as plain characters between the rollers

Restyle the ticker

Both lanes take className on the root, a column flex container around the number and its label. The label reads from the shared mono token in surfaces.tsx.

<NumberTicker className="gap-1" /* ... */ />

API reference

Tool-call render props

PropTypeDescription
argsTArgsParsed arguments. Partial while the model is still streaming them.
argsTextstringRaw JSON argument text streamed by the model.
resultTResult | undefinedThe tool's return value once it completes. undefined while running.
statusToolCallMessagePartStatusstatus.type is "running", "requires-action", "complete", or "incomplete".
toolNamestringName of the tool the model called.
toolCallIdstringStable id for this invocation.
isErrorboolean | undefinedWhether result represents a tool execution error.

Register the renderer on a toolkit entry's render field and attach the toolkit with Tools({ toolkit }). See Tool UI for the full render-prop surface, including addResult, human tools, and approval gates.