~/project$ pnpm test✓ src/utils.test.ts (5 tests) 23ms
✓ src/api.test.ts (12 tests) 156ms
✓ src/components.test.ts (8 tests) 89ms
Test Files 3 passed (3)
Tests 25 passed (25)
Start at 10:23:45
Duration 312ms<Terminal command="pnpm test" stdout={`[32m✓[0m src/utils.test.ts [90m(5 tests)[0m [33m23ms[0m[32m✓[0m src/api.test.ts [90m(12 tests)[0m [33m156ms[0m[32m✓[0m src/components.test.ts [90m(8 tests)[0m [33m89ms[0m[1mTest Files[0m [32m3 passed[0m (3)[1m Tests[0m [32m25 passed[0m (25)[1m Start at[0m 10:23:45[1m Duration[0m 312ms`} exitCode={0} durationMs={312} cwd="~/project"/>Raw terminal output was not designed for conversation. Shell results lose their ANSI colors, exit codes, and stderr separation the moment they land in a chat message. Terminal renders command output the way you would see it in a real shell, colored, structured, and auto-collapsed when it gets long, so it does not take over the conversation.
Role: Information. For displaying data without user input. See Design Guidelines for how component roles work.
Run this once from your project root.
npx shadcn@latest add @tool-ui/terminalRender Terminal in your UI with tool-compatible props.
import { Terminal } from "@/components/tool-ui/terminal";export function MyComponent() { return ( <Terminal id="my-terminal-example" command="ls -la" stdout={`total 16drwxr-xr-x 3 user user 4096 Dec 3 10:23 .drwxr-xr-x 12 user user 4096 Dec 3 10:20 ..-rw-r--r-- 1 user user 220 Dec 3 10:23 package.json`} exitCode={0} durationMs={45} cwd="~/project" /> );}Registration tells assistant-ui which component to render when a tool returns command output. Without it, tool results appear as raw JSON.
// Backend toolimport { tool, jsonSchema } from "ai";const showTerminal = tool({ description: "Show a command result", inputSchema: jsonSchema<{}>({ type: "object", properties: {}, additionalProperties: false, }), async execute() { return { id: "terminal-1", command: "pnpm test", cwd: "~/project", exitCode: 0, durationMs: 1288, stdout: "\u001b[32m✓\u001b[0m 42 tests passed", }; },});// Frontend with assistant-uiimport { type Toolkit } from "@assistant-ui/react";import { Terminal } from "@/components/tool-ui/terminal";import { safeParseSerializableTerminal } from "@/components/tool-ui/terminal/schema";import { createResultToolRenderer } from "@/components/tool-ui/shared";export const toolkit: Toolkit = { showTerminal: { type: "backend", render: createResultToolRenderer({ safeParse: safeParseSerializableTerminal, render: (parsedResult) => ( <> <Terminal {...parsedResult} /> </> ), }), },};Renders colored output exactly as it appears in your shell
Green for success (0), red for any non-zero code
Badge showing how long the command took
Stderr rendered separately with distinct styling
Prop
Type
Prop
Type
Prop
Type
Prop
Type
Use ToolUI.LocalActions to compose external actions next to Terminal.
Terminal automatically renders ANSI escape codes for colored output:
import { Terminal } from "@/components/tool-ui/terminal";const coloredOutput = "\x1b[32m✔\x1b[0m Success\n\x1b[31m✗\x1b[0m Error";<Terminal id="my-terminal" command="npm test" stdout={coloredOutput} exitCode={0}/>;Common ANSI codes:
\x1b[32m - Green text\x1b[31m - Red text\x1b[33m - Yellow text\x1b[36m - Cyan text\x1b[1m - Bold text\x1b[0m - Reset formatting0 - Success (green)1 - General error (red)2 - Misuse of shell builtins (red)126 - Command not executable (red)127 - Command not found (red)130 - Interrupted with Ctrl+C (red)