Code Block
A syntax-highlighted code block component powered by Shiki. Supports static code strings and streaming content, line numbers, and copy-to-clipboard.
const greeting = "Hello, world!";
console.log(greeting);import typescript from "@shikijs/langs/typescript";
import { CodeBlock } from "@tenstorrent/vesper/code-block";
export default function CodeBlockDemo() {
return (
<CodeBlock lang={typescript}>
{'const greeting = "Hello, world!";\nconsole.log(greeting);'}
</CodeBlock>
);
}CodeBlock component itself!Options
| Prop | Type | Description | Default |
|---|---|---|---|
children | string | () => ReadableStream<string> | Promise<ReadableStream<string>> | 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 <div> element.
To render a short snippet of code inline with other text, reach for the Code component, and for a single copyable command, the Snippet component.
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:
Hello, world!import { CodeBlock } from "@tenstorrent/vesper/code-block";
export default function BasicCodeBlock() {
return <CodeBlock>Hello, world!</CodeBlock>;
}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:
const count = 42;
console.log(count);import javascript from "@shikijs/langs/javascript";
import { CodeBlock } from "@tenstorrent/vesper/code-block";
export default function HighlightedCodeBlock() {
return (
<CodeBlock lang={javascript}>
{"const count = 42;\nconsole.log(count);"}
</CodeBlock>
);
}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":
● vesper v0.2.0
✓ compiled 42 modules
! 1 warning found
✗ failed to resolve ./missing-moduleimport { 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 <CodeBlock lang="ansi">{BUILD_LOGS}</CodeBlock>;
}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:
import { CodeBlock } from "@tenstorrent/vesper/code-block";
import customGrammar from "./custom-grammar.json";
export default function CustomGrammarCodeBlock({ code }: { code: string }) {
return <CodeBlock lang={[customGrammar]}>{code}</CodeBlock>;
}With line numbers
Passing showLineNumbers renders a line number gutter down the left-hand side of the code block:
def greet(name: str) -> str:
message = f"Hello, {name}!"
print(message)
return message
greet("world")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 (
<CodeBlock lang={python} showLineNumbers>
{CODE}
</CodeBlock>
);
}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<string> 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:
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<string>({
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 (
<div
style={{
display: "flex",
flexDirection: "column",
gap: "var(--vesper-spacing-4)",
}}
>
<Button size="sm" onClick={() => setRun((run) => run + 1)}>
Replay stream
</Button>
<CodeBlock key={run} lang={typescript}>
{createCodeStream}
</CodeBlock>
</div>
);
}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:
{
"name": "@tenstorrent/vesper",
"version": "0.2.0"
}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 (
<CodeBlock lang={json} copyOnHover>
{CODE}
</CodeBlock>
);
}