# Code Block A syntax-highlighted code block component powered by Shiki. Supports static code strings and streaming content, line numbers, and copy-to-clipboard. ```tsx demo import typescript from "@shikijs/langs/typescript"; import { CodeBlock } from "@tenstorrent/vesper/code-block"; export default function CodeBlockDemo() { return ( {'const greeting = "Hello, world!";\nconsole.log(greeting);'} ); } ``` > [!NOTE] > > The code samples on this page are rendered using the `CodeBlock` component itself! ## Options | Prop | Type | Description | Default | | ----------------- | --------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- | -------- | | `children` | `string \| () => ReadableStream \| Promise>` | The code to render. Can be a string or a factory function returning a `ReadableStream` for streaming content. | `""` | | `lang` | `LanguageRegistration[] \| "text" \| "ansi"` | The language syntax for highlighting. Import grammar objects from `@shikijs/langs`. Use `"text"` for plain text or `"ansi"` for ANSI escape codes. | `"text"` | | `showLineNumbers` | `boolean` | Whether to show line numbers on the left-hand side. | `false` | | `copyOnHover` | `boolean` | When `true`, hides the copy-to-clipboard button until the code block is hovered. | `false` | All other props are forwarded to the underlying `
` element. To render a short snippet of code inline with other text, reach for [the `Code` component](./code.mdx), and for a single copyable command, [the `Snippet` component](./snippet.mdx). ## Examples ### Basic usage Render a `CodeBlock` by passing it plain text. Without a `lang`, the code is rendered as-is with no syntax highlighting. Every code block renders a copy-to-clipboard button in its top-right corner, which copies the code exactly as it was passed in: ```tsx demo import { CodeBlock } from "@tenstorrent/vesper/code-block"; export default function BasicCodeBlock() { return Hello, world!; } ``` ### Syntax highlighting `CodeBlock` ships without any built-in grammars, and expects you to provide them yourself. Individual grammars can be imported from `@shikijs/langs/*` and supplied to the component. For example, to achieve `javascript` syntax highlighting you would import the grammar and pass it to the component via the `lang` prop: ```tsx demo import javascript from "@shikijs/langs/javascript"; import { CodeBlock } from "@tenstorrent/vesper/code-block"; export default function HighlightedCodeBlock() { return ( {"const count = 42;\nconsole.log(count);"} ); } ``` > [!NOTE] > > Importing grammars individually keeps your bundle to only the languages you actually render. #### Plain text and ANSI Plain text and ANSI come prebundled and don't require supplying any grammar objects. You can simply set the `lang` prop to `"text"` or `"ansi"`: ```tsx demo import { CodeBlock } from "@tenstorrent/vesper/code-block"; const BUILD_LOGS = [ "\u001b[1;36m● vesper\u001b[0m \u001b[2mv0.2.0\u001b[0m", "\u001b[32m✓\u001b[0m compiled 42 modules", "\u001b[33m!\u001b[0m 1 warning found", "\u001b[31m✗\u001b[0m failed to resolve \u001b[1m./missing-module\u001b[0m", ].join("\n"); export default function AnsiCodeBlock() { return {BUILD_LOGS}; } ``` This is what you want for output captured from a terminal, such as build logs or test runs, where the color information is already encoded in the text itself. #### Custom grammars You can provide your own custom grammars by passing them into the `lang` prop as an array of TextMate grammar objects: ```tsx import { CodeBlock } from "@tenstorrent/vesper/code-block"; import customGrammar from "./custom-grammar.json"; export default function CustomGrammarCodeBlock({ code }: { code: string }) { return {code}; } ``` [tm-grammars package on GitHub](https://github.com/shikijs/textmate-grammars-themes/tree/main/packages/tm-grammars) ### With line numbers Passing `showLineNumbers` renders a line number gutter down the left-hand side of the code block: ```tsx demo import python from "@shikijs/langs/python"; import { CodeBlock } from "@tenstorrent/vesper/code-block"; const CODE = `def greet(name: str) -> str: message = f"Hello, {name}!" print(message) return message greet("world")`; export default function NumberedCodeBlock() { return ( {CODE} ); } ``` Line numbers are rendered decoratively, so they are never included in the text copied to the clipboard. ### Streaming content To stream code as it arrives (build logs, LLM output, etc.), pass a factory function that returns a `ReadableStream` instead of a string. Tokens are highlighted as they arrive, and the block scrolls to follow the output unless the user scrolls away from the bottom: ```tsx demo import { useState } from "react"; import typescript from "@shikijs/langs/typescript"; import { Button } from "@tenstorrent/vesper/button"; import { CodeBlock } from "@tenstorrent/vesper/code-block"; const CODE = `async function getUser(id: string) { const response = await fetch(\`/api/users/\${id}\`); if (!response.ok) throw new Error("Request failed"); return response.json(); }`; /** * A factory is used instead of a raw stream because a stream can only be read * once: this way the component can create a fresh one whenever it needs to */ function createCodeStream() { let remaining = CODE; return new ReadableStream({ async start(controller) { while (remaining.length) { controller.enqueue(remaining.slice(0, 8)); remaining = remaining.slice(8); await new Promise((resolve) => setTimeout(resolve, 60)); } controller.close(); }, }); } export default function StreamedCodeBlock() { const [run, setRun] = useState(0); return (
{createCodeStream}
); } ``` ### Showing clipboard button on hover The copy-to-clipboard button is always visible by default; pass `copyOnHover` to hide it until the code block is hovered: ```tsx demo import json from "@shikijs/langs/json"; import { CodeBlock } from "@tenstorrent/vesper/code-block"; const CODE = `{ "name": "@tenstorrent/vesper", "version": "0.2.0" }`; export default function HoverCopyCodeBlock() { return ( {CODE} ); } ```