DocumentationComponentsTheme Creator
Theme CreatorGitHubIntroduction
InstallationUsageThemingMotion
Components
CardCarouselTableDataGridCodeBlock
AI agentsContributing
  1. Components
  2. CodeBlock

CodeBlock

Syntax-highlighted code display with single-file and multi-tab modes, a copy button, line highlighting, wrap and collapse — on a self-contained neutral plate that can follow or lock its theme.

$ pnpm add @tessinaui/ui

When to use

  • Static code people will read or copy: a snippet in docs, an API example, a code answer in an AI chat. It highlights, copies, wraps and collapses, and it paints its own neutral plate so it reads on any surface.
  • An install command or one-liner. Hide the header with showHeader={false} and the copy control floats over the code (Command).
  • The same command for several tools or files. Give tabs one entry each, for package managers or a set of files, instead of stacking blocks (Multi-tab).
  • A long listing that should not dominate the page. Cap it with maxHeight so the block collapses behind a show more toggle (Collapsible).
  • Pointing at the lines that matter. Use highlightLines with showLineNumbers so the prose can refer to a line.

When not to use

  • Inline code inside a sentence. Use a <code> element; CodeBlock is always a block.
  • Code the reader edits. The block is read-only. Use a Textarea for anything the user types or changes.
  • A live terminal or log stream. A scrolling console with timestamps and filters is a log viewer, not a code block. Render the stream in its own component.
  • A diff. Added and removed rows with dual gutters and split or unified views are their own control; a CodeBlock shows a snapshot, not a change.
  • A single short value people can select easily, such as a key shown once. A plain <code> element is lighter than a block with a header.

Installation

Terminal
pnpm add @tessinaui/ui react-syntax-highlighter @types/react-syntax-highlighter

Usage

example.tsx
import { CodeBlock } from "@tessinaui/ui";
example.tsx
<CodeBlock
  language="typescript"
  filename="utils.ts"
  code={`export const add = (a: number, b: number) => a + b;`}
/>

Examples

Default

A single-file code block with a filename and copy button.

Command

Hide the header with showHeader={false} for a bare snippet — an install command or a one-liner — with the copy control floating over the code. This is the dominant shape in developer docs.

Multi-tab

Provide tabs to switch between multiple files, each with its own language and copy state. The tab bar is one keyboard tab stop; arrow keys move between files.

Package managers

The tabs pattern is how a docs page offers the same command for npm, pnpm, yarn and bun.

Line highlighting

Highlight specific lines with highlightLines, paired with showLineNumbers for clarity.

Collapsible

Cap the height with maxHeight to reveal an expand/collapse toggle for long blocks. showMoreLabel / showLessLabel relabel the toggle.

Wrap

By default a long line scrolls horizontally. Set showLineWrap to wrap it instead — useful for prose-like commands where horizontal scrolling hides the tail.

Sizes

Three sizes — sm, md, and lg — scale the whole block: code padding, the code type scale, and the chrome around it (header height, filename, copy control, tab bar, expand row). Type follows the shared data-display ladder, with the header one step below the code it labels.

Header actions

Pass any controls to headerActions — a download button, an "open in playground" link, a language Select — and they sit in the header before the copy button.

Theme

The plate is self-contained. theme="auto" (default) follows the page; theme="dark" or theme="light" locks it — the dark-code-on-a-light-docs-page convention.

Error

A clipboard write can be refused — sandboxed iframes, unfocused pages, embedded webviews. The copy control then shows Failed and announces "Couldn't copy to clipboard" through the same polite live region that confirms a success, so nobody pastes stale text believing the copy worked. writeToClipboard replaces the built-in write for hosts with their own clipboard bridge; resolve false or throw to report a failure, as this example does.

Loading

CodeBlockSkeleton renders a placeholder that matches the block's box while code is being fetched.

On color

The block stands on a coloured Surface as its own panel: its fills are opaque and its syntax palette and chrome inks are the plate's own, so nothing shows through and nothing re-inks. Header actions inside the block read the block's plate, never the surface around it.

Language identifiers

language accepts any identifier supported by Prism. Common values:

typescript · tsx · javascript · jsx · bash · shell · python · css · scss · json · yaml · markdown · html · sql · rust · go

Pass "text" or omit language in a tab to display plain text with no highlighting.

API Reference

CodeBlock

Either code + language (single-file) or tabs (multi-file) is required. The root forwards className and any other div attributes (data-*, id, aria-*).

PropTypeDefaultDescription
codestring—Code string — single-file mode
languagestring—Prism language identifier
filenamestring—Shown in the header; when omitted the language badge is used
highlightLinesnumber[][]1-indexed line numbers to highlight
tabsCodeBlockTab[]—Multi-tab mode — replaces code, language, filename
theme"auto" | "light" | "dark""auto"auto follows the page; light / dark lock the plate
size"sm" | "md" | "lg""md"Padding, code type scale, and header/tab/expand chrome
rounded"none" | "sm" | "md" | "lg" | "full""lg"Corner radius — full caps at the panel radius
showLineNumbersbooleanfalseRenders the line-number gutter
showLineWrapbooleanfalseWrap long lines instead of scrolling horizontally
showHeaderbooleantrueSingle-code mode only — when false the copy control floats
showLangBadgebooleantrueShow the language badge when no filename is set
headerActionsReactNode—Extra controls in the header, before the copy button
writeToClipboard(code: string) => boolean | Promise<boolean>—Replaces the built-in clipboard write; false or a throw shows and announces a failed copy
maxHeightstring | number—Caps block height and adds an expand/collapse toggle
showMoreLabelstring"Show more"Expand-control label when collapsed
showLessLabelstring"Show less"Expand-control label when expanded
defaultCollapsedbooleantrueInitial collapsed state when maxHeight is set
collapsedboolean—Controlled collapsed state
onCollapsedChange(collapsed: boolean) => void—Fires when the expand/collapse button is clicked

CodeBlockSkeleton

Mirrors the block's box (§2b). Takes size, rounded, theme, plus lines (default 6), showHeader, showFilename, showLangBadge, showLineNumbers and maxHeight, and forwards className + div attributes.

CodeBlockTab

example.ts
interface CodeBlockTab {
  name: string;              // Label shown in the tab button
  code: string;              // Code content for this tab
  language?: string;         // Prism language (defaults to "text")
  highlightLines?: number[]; // 1-indexed lines to highlight in this tab
}

Accessibility

Pattern: WAI-ARIA Tabs in multi-file mode; a single block is a named group.

  • Multi-file mode is a real tablist. tabs renders role="tablist" (aria-label="Code tabs", horizontal) with a roving tabindex — one tab stop, arrow keys between files — and each role="tab" carries aria-selected and aria-controls. The code is the role="tabpanel", labelled by its tab.
  • A single block is a named group, named by filename, else the language, else "Code".
  • The code region is focusable (tabIndex={0}), so a keyboard user can scroll a long line that does not wrap. Set showLineWrap when horizontal scrolling would hide the part that matters.
  • Copy confirms out loud. The button's name swaps from "Copy code" to "Copied to clipboard", and because a swapped name alone is not reliably announced, a visually hidden polite role="status" region speaks the same words. It only does so when the clipboard write succeeded.
  • A failed copy is announced too. The control shows Failed, its name becomes "Couldn't copy to clipboard", and the same region speaks it. The region is mounted empty from first render, and a repeated failure is announced again.
  • The expand toggle (maxHeight) carries aria-expanded and aria-controls; showMoreLabel / showLessLabel are its name.
  • Decoration is hidden. The copy and expand icons are aria-hidden; CodeBlockSkeleton is a single role="status" with a visually hidden "Loading", written 150ms after mount.
  • Contrast — the syntax palette is literal colour, not tokens, so it is gated token-by-token in both themes by code-block.contrast.test.tsx instead of audit:contrast: every ink must clear AA on the bare plate and on the highlighted-line band.

Keyboard

KeyContextAction
Tab / Shift+TabAnywhereTab bar (one stop), headerActions, copy button, code region, expand toggle — natural order
← / →Tab barPrevious / next file, wrapping at the ends
Home / EndTab barFirst / last file
Enter / SpaceCopy button, expand toggleCopies the code / toggles the height cap
← / →Focused code regionScrolls a long line horizontally

AT matrix

ATStatus
VoiceOver + Safari (macOS)Passed 2026-09-14 after a fix — the copy button reads "Copy code, button" and copying announces "Copied to clipboard". The result region used to sit inside the button, where WebKit never exposes a live region, so copying was silent
VoiceOver + Chrome (macOS)UNTESTED
NVDA + Chrome (Windows)UNTESTED — no Windows host available
JAWSUNTESTED

Status stays beta until one VoiceOver and one NVDA pass are on record (QA flow §5.6).

DataGridChart

On this page

When to useWhen not to useInstallationUsageExamplesDefaultCommandMulti-tabPackage managersLine highlightingCollapsibleWrapSizesHeader actionsThemeErrorLoadingOn colorLanguage identifiersAPI ReferenceCodeBlockCodeBlockSkeletonCodeBlockTabAccessibilityKeyboardAT matrix