CodeBlock
code-blockDisplays multiline code with an optional language label and copy action.
Usage
Basic usage
Pass in multiple lines of code (wrap with \n), automatic syntax coloring + copy button in the upper right corner.
<Lens zoom={1.8}>
<img src="/photo.jpg" />
</Lens>const code = `<Lens zoom={1.8}>
<img src="/photo.jpg" />
</Lens>`;
<CodeBlock code={code} />With language tag
lang Displays the language identifier in the upper left corner and affects coloring rules.
import { Button } from "@hulianui/ui";
// Click counting example
export function Demo() {
const [n, setN] = useState(0);
return <Button onClick={() => setN(n + 1)}> clicked {n} times</Button>;
}<CodeBlock code={code} lang="tsx" />Shell command
lang="bash" Colorize the command name according to Shell rules with flag.
# Install and build
pnpm add @hulianui/ui
pnpm --filter @hulianui/ui build<CodeBlock code={shell} lang="bash" />Python
lang="python" (py and python3 are aliases) highlights Python lexically: # comments, triple-quoted docstrings, f-string literals, decorators, and builtins.
# Number guessing: halve the range every round
import functools
import random
SECRET = random.randint(1, 100)
@functools.cache
def guess(n: int) -> str:
"""Compare the guess and return a hint.
n is the number the player guessed this round.
"""
if not isinstance(n, int):
raise TypeError("Integers only")
if n == SECRET:
return f"Correct, the number is {n}"
return "Too high" if n > SECRET else "Too low"
for i in range(0, 3):
print(guess(random.randint(1, 100)))<CodeBlock code={python} lang="python" />With line numbers
lineNumbers shows a gutter so prose can point at "line 12". Line numbers cannot be selected and never reach the clipboard; the gutter width follows the digits of the largest line number; the gutter stays pinned to the left while the code scrolls horizontally. Use lineNumbers={{ start: 120 }} when the snippet is cut from the middle of a file.
# Number guessing: halve the range every roundimport functoolsimport randomSECRET = random.randint(1, 100)@functools.cachedef guess(n: int) -> str: """Compare the guess and return a hint. n is the number the player guessed this round. """ if not isinstance(n, int): raise TypeError("Integers only") if n == SECRET: return f"Correct, the number is {n}" return "Too high" if n > SECRET else "Too low"for i in range(0, 3): print(guess(random.randint(1, 100)))<Lens zoom={1.8}> <img src="/photo.jpg" /></Lens><CodeBlock code={python} lang="python" lineNumbers />
// The snippet starts at line 120
<CodeBlock code={snippet} lang="python" lineNumbers={{ start: 120 }} />Coloring Off / Not Copyable
highlight={false} renders plain text; copyable={false} removes the copy button.
import { Button } from "@hulianui/ui";
// Click counting example
export function Demo() {
const [n, setN] = useState(0);
return <Button onClick={() => setN(n + 1)}> clicked {n} times</Button>;
}<Lens zoom={1.8}>
<img src="/photo.jpg" />
</Lens><>
<CodeBlock code={code} highlight={false} />
<CodeBlock code={code} copyable={false} />
</>When to use
Use CodeBlock for multiline snippets with built-in syntax coloring, a language label, and one-click copy. Use Snippet for a single-line command or identifier, Code for inline code, or CodeDiff for before-and-after changes.
Import
import { CodeBlock, HighlightedCode, tokenizeCode, type CodeToken, type CodeTokenType } from "@hulianui/ui"Props
| Name | Type | Default | Description |
|---|---|---|---|
| code* | string | - | Code text; use \n for multiple lines. |
| lang | string | - | Language label shown in the upper-right corner, such as "tsx". It also selects JavaScript-family, shell, or Python highlighting rules. |
| copyable | boolean | true | Whether to show the copy button. |
| highlight | boolean | true | Whether to apply syntax coloring; disable it for plain text. |
| lineNumbers | boolean | { start?: number } | false | Whether to show a line-number gutter. Pass { start: 120 } so a snippet is numbered from a given line; the gutter width follows the digits of the largest line number. |
| className | string | - | Additional class name for the container. |
Highlighted languages
| lang | Rules |
|---|---|
js jsx ts tsx json | JavaScript family |
bash sh shell zsh console | Shell (command names and flags get separate colors) |
py python python3 | Python (# comments, triple-quoted docstrings, f/r/b prefixed strings, decorators, builtins, and 0b/0o/0x/underscore/imaginary literals) |
| Anything else | Approximated with the JavaScript-family rules |
Usage guidelines
- A `lang` without a dedicated branch is approximated, not supported. Every language outside the table above is scanned with the JavaScript-family rules: the
#comments ofyaml/toml/ini/dockerfileand the--comments of SQL are not recognized, so comment bodies are scanned as code and words inside them may even be colored as JavaScript keywords. Either accept that, or passhighlight={false}so nothing is colored wrongly; open an issue if you need a real branch for your language. - Line numbers are decoration, not content. The gutter is
aria-hiddenand cannot be selected: screen readers skip it, and selecting the whole block never drags1 2 3into the clipboard. The copy button always copies the originalcode. Conversely, do not treat the gutter as a source of copyable data. - The gutter stays pinned to the left (
sticky left-0plus an opaque background). Line numbers survive horizontal scrolling of long lines; the cost is that the scrolled code is hidden under a narrow strip. That trade is deliberate: on a long line, seeing the line number matters more than that strip of code. - The copy button uses
navigator.clipboard, which only exists in a secure context (HTTPS or localhost); on a plain HTTP page in a local network, clicking it copies nothing.
Related
Playground
import { Button } from "@hulianui/ui";
// Click counting example
export function Demo() {
const [n, setN] = useState(0);
return <Button onClick={() => setN(n + 1)}> clicked {n} times</Button>;
}<CodeBlock code={code} lang="tsx" />