CodeBlock

Syntax-highlighted code display with clipboard copy, horizontal scrolling, line emphasis, diff rendering, collapsible folding, and tabbed alternatives.
The variant axis picks the surface (terminal > numbered > inline > plain, first match wins); compact tightens the density; copy, wrap, diff, collapsible, and attached stack orthogonally. language drives the in-kit highlighter (ts/tsx/js/jsx, json, bash, css, html, python; anything else renders monochrome), and long lines scroll horizontally instead of truncating.
When a line is wider than the block, Tab reaches the scrollable code and the platform's arrow keys scroll it; while it has keyboard focus, the block's card draws the theme's focus ring. Short content, wrap, and inline do not add a scrolling tab stop. Native scrolling continues to use the native ScrollView.
ts
const theme = getTheme();
setTheme(theme === "dark" ? "light" : "dark");
iOS
ts
const theme = getTheme();
setTheme(theme === "dark" ? "light" : "dark");
Android
ts
const theme = getTheme();
setTheme(theme === "dark" ? "light" : "dark");
Web
tsx
<CodeBlock
language="ts"
code={`const theme = getTheme();
setTheme(theme === "dark" ? "light" : "dark");`}
/>

Props

Prop
Description
code
string
The code to render. Newlines are preserved (RN Text honors "\n").
filename
string
Optional filename label shown in a header bar above the surface. Honored by the plain and numbered variants (and the terminal chrome label); the inline pill has no header. When both are set, `filename` takes the header label and `language` renders as a trailing badge.
language
string
The snippet's language. Drives syntax highlighting (ts/tsx/js/jsx, json, bash/sh, css, html, python — anything else renders monochrome) and the header/terminal label when no `filename` is given.
terminal
boolean
Variant (pick one; default is the plain block).
numbered
boolean
Variant (pick one; default is the plain block).
inline
boolean
Variant (pick one; default is the plain block).
compact
boolean
Density (pick one; default is the comfortable ramp).
copy
boolean
Show a copy affordance (header-hosted when a header/chrome exists, else pinned top-right).
wrap
boolean
Soft-wrap long lines instead of scrolling them horizontally. An overflowing unwrapped block is a keyboard tab stop; arrow keys use the platform's normal scrolling.
diff
boolean
Render the code as a unified diff: the first column of every line is the marker ("+" added, "-" removed, anything else context). Added/removed rows tint green/red, markers stay out of the selection, and the copy chip yields the post-change code (context + added lines, markers stripped).
attached
boolean
Square the top edge (no top radius/border) to sit flush under an adjoining bar.
highlightLines
ReadonlyArray<number | string>
Emphasize lines: entries are 1-based line numbers or "start-end" ranges (e.g. `[2, "5-7"]`). Numbers refer to the displayed numbering, so they respect `startLine`. Plain and numbered variants only.
startLine
number
First displayed line number for the numbered variant (default 1).
collapsible
boolean
Fold long blocks behind a "Show all" expander (plain + numbered variants).
collapsedLines
number
How many lines a collapsed block shows (default 8).
expanded
boolean
Controlled expanded state for `collapsible`.
defaultExpanded
boolean
Uncontrolled initial expanded state (default false).
onExpandedChange
(expanded: boolean) => void
Called when the expander toggles (fires in both modes).
tabs
ReadonlyArray<CodeBlockTab>
Labeled code alternatives rendered as a tab strip in the header (or the terminal chrome) — the classic npm/yarn/bun switcher. Each tab supplies its own code and optional language/filename.
active
number
Controlled active tab index.
defaultActive
number
Uncontrolled initial tab index (default 0).
onTabChange
(index: number) => void
Called with the next tab index when a tab is selected (fires in both modes).
prompt
string
The terminal prompt marker (default "$"). Lines starting with it render as commands.
onCopy
(code: string, event: GestureResponderEvent) => void
Called when the copy affordance is pressed, with the copied text. The chip also writes the clipboard itself (navigator.clipboard on web, the optional expo-clipboard peer on native), so this is for side effects, not plumbing.
testID
string
E2E hook forwarded to the root element.
style
LayoutStyle
Composition within a parent only, never a restyle hook and never a width: the parent layout container provides the bounds.

Do & Don’t

Plain

Don’t
const theme = getTheme(); setTheme(theme === "dark" ? "light" : "dark");
A paragraph collapses the line breaks and indentation, so multi-line code reads as one run-on string.
Do
const theme = getTheme();
setTheme(theme === "dark" ? "light" : "dark");
Use CodeBlock so whitespace, line breaks, and indentation survive verbatim.

Terminal

Don’t
$ npm install @nannier/canvas
Selectable prompt text means a reader who copies the line drags the $ marker into their shell.
Do
bash
$
npm install @nannier/canvas
Use the terminal variant: the prompt stays out of the selection, so a copy yields only the command, not the shell glyph.

Numbered

Don’t
1
2
const theme = getTheme();
setTheme(theme);
Selectable line numbers get swept into the selection and pasted as 1 2 ahead of every line.
Do
1
2
const theme = getTheme();
setTheme(theme);
Use the numbered variant: the gutter stays out of the selection, so copying the block returns clean, runnable code.

Diff

Don’t
-const theme = "light";
+const theme = getTheme();
setTheme(theme);
Hand-colored +/- rows leave the markers selectable, so a copied block pastes broken code.
Do
ts
-
const theme = "light";
+
const theme = getTheme();
setTheme(theme);
Use diff mode: markers stay out of the selection and the copy chip yields the post-change code.

Tabs

Don’t
bash
$
npm install @nannier/canvas
bash
$
yarn add @nannier/canvas
bash
$
bun add @nannier/canvas
Stacked one-per-manager blocks repeat the same install three times and triple the page height.
Do
$
npm install @nannier/canvas
One tabbed block keeps the package-manager alternatives in one place; the reader picks once.

Inline

Don’t
Run
npm install @nannier/canvas && npm run build && npm run preview
to start.
A long, multi-step command crammed inline wraps mid-token and offers no horizontal scroll.
Do
Run the setup command:
npm install @nannier/canvas
npm run build
Reserve inline code for short tokens; move anything multi-line into a block.