<CodeBlock code={`import { useState } from "react";export function Counter() { const [count, setCount] = useState(0); return ( <button onClick={() => setCount(c => c + 1)}> Count: {count} </button> );}`} language="typescript" filename="Counter.tsx" showLineNumbers={true}/>Powered by Shiki with support for 100+ languages
Draw attention to specific lines for explanations or bug indicators
Auto-collapse long snippets to keep conversations scannable
Show filename and language badge for context
Copy components/tool-ui/code-block and the shared directory into your project. The shared folder contains utilities used by all Tool UI components. The tool-ui directory should sit alongside your shadcn ui directory.
This component requires the following shadcn/ui components:
pnpm dlx shadcn@latest add buttonIt also uses Radix Collapsible directly:
pnpm add @radix-ui/react-collapsibleInstall dependencies (Shiki for syntax highlighting):
pnpm add shikiimport { CodeBlock } from "@/components/tool-ui/code-block";export function MyComponent() { return ( <CodeBlock id="my-code-example" code={`function hello(name: string) { return \`Hello, \${name}!\`;}`} language="typescript" filename="hello.ts" showLineNumbers={true} /> );}Define a tool that returns Code Block props, then render the component with runtime validation.
// Backend toolimport { tool, jsonSchema } from "ai";const showCodeBlock = tool({ description: "Show a code snippet", inputSchema: jsonSchema<{}>({ type: "object", properties: {}, additionalProperties: false, }), async execute() { return { id: "code-block-1", code: "console.log('hello');", language: "javascript", filename: "hello.js", highlightLines: [1], }; },});// Frontend with assistant-uiimport { makeAssistantToolUI } from "@assistant-ui/react";import { CodeBlock, CodeBlockErrorBoundary, parseSerializableCodeBlock,} from "@/components/tool-ui/code-block";function ParsedCodeBlock({ result }: { result: unknown }) { const props = parseSerializableCodeBlock(result); return <CodeBlock {...props} />;}export const ShowCodeBlockUI = makeAssistantToolUI({ toolName: "showCodeBlock", render: ({ result }) => { // Tool outputs stream in; `result` will be `undefined` until the tool resolves. if (result === undefined) { return <CodeBlock id="loading" code="" language="text" isLoading />; } return ( <CodeBlockErrorBoundary> <ParsedCodeBlock result={result} /> </CodeBlockErrorBoundary> ); },});Prop
Type
Prop
Type
Prop
Type
CodeBlock supports all languages included with Shiki, including:
See Shiki documentation for the complete list.
pnpm dlx shadcn@latest add buttonpnpm add @radix-ui/react-collapsible<CodeBlock
code={`import { useState } from "react";
export function Counter() {
const [count, setCount] = useState(0);
return (
<button onClick={() => setCount(c => c + 1)}>
Count: {count}
</button>
);
}`}
language="typescript"
filename="Counter.tsx"
showLineNumbers={true}
/>pnpm add shikiimport { CodeBlock } from "@/components/tool-ui/code-block";
export function MyComponent() {
return (
<CodeBlock
id="my-code-example"
code={`function hello(name: string) {
return \`Hello, \${name}!\`;
}`}
language="typescript"
filename="hello.ts"
showLineNumbers={true}
/>
);
}// Backend tool
import { tool, jsonSchema } from "ai";
const showCodeBlock = tool({
description: "Show a code snippet",
inputSchema: jsonSchema<{}>({
type: "object",
properties: {},
additionalProperties: false,
}),
async execute() {
return {
id: "code-block-1",
code: "console.log('hello');",
language: "javascript",
filename: "hello.js",
highlightLines: [1],
};
},
});
// Frontend with assistant-ui
import { makeAssistantToolUI } from "@assistant-ui/react";
import {
CodeBlock,
CodeBlockErrorBoundary,
parseSerializableCodeBlock,
} from "@/components/tool-ui/code-block";
function ParsedCodeBlock({ result }: { result: unknown }) {
const props = parseSerializableCodeBlock(result);
return <CodeBlock {...props} />;
}
export const ShowCodeBlockUI = makeAssistantToolUI({
toolName: "showCodeBlock",
render: ({ result }) => {
// Tool outputs stream in; `result` will be `undefined` until the tool resolves.
if (result === undefined) {
return <CodeBlock id="loading" code="" language="text" isLoading />;
}
return (
<CodeBlockErrorBoundary>
<ParsedCodeBlock result={result} />
</CodeBlockErrorBoundary>
);
},
});