Code to read and copy, with a file name or tabs, line numbers and marked lines.
$ pnpm dlx shadcn@latest add @dashral/code-blockInstallation
$ pnpm dlx shadcn@latest add @dashral/code-blockAdds the component, the components it builds on, its npm packages and the theme tokens it uses. Or by its full URL: https://dashral.com/r/code-block.json
Usage
import { CodeBlock } from "@/components/dashral/code-block"
<CodeBlock filename="app/page.tsx" language="tsx" code={source} lineNumbers highlightLines={[4, 5]} />Using the npm package? Import from @dashral/ui/components/code-block instead, here and in the examples; the rest is the same.
Examples
Install command
tabs: one command per package manager; the arrow keys move between them.
Line numbers and marked lines
lineNumbers, and highlightLines for the lines a step changed.
export async function GET(request: Request) { const { searchParams } = new URL(request.url) const page = Number(searchParams.get("page") ?? 1) const invoices = await db.invoice.findMany({ where: { status: "open" }, orderBy: { due: "asc" }, skip: (page - 1) * 20, take: 20, }) return Response.json({ invoices, page })}Without a header
The copy button floats in the corner; wrap breaks long lines.
DATABASE_URL="postgres://app:secret@localhost:5432/app"RESEND_API_KEY="re_123"NEXT_PUBLIC_SITE_URL="http://localhost:3000"[12:04:31] build ✓ compiled 412 modules in 3.2s — the longest line here keeps going so that it wraps instead of scrolling sideways when wrap is on[12:04:33] deploy ✓ uploaded to sin1[12:04:35] deploy ✓ live at https://app.example.comAPI reference
CodeBlock
Also takes every prop of <div>(opens in a new tab).
| Prop | Type | Default |
|---|---|---|
code | stringThe code, as plain text: what's shown and copied. | — |
filename | stringA file name in the header. | — |
language | stringShown at the header's end, and set as data-language on the <pre>. | — |
tabs | { label, code, language? }[]Several snippets, one at a time, e.g. a command per package manager. In place of code. | — |
defaultTab / onTabChange | string / (label) => voidThe tab shown first, and a call when another is picked. | the first |
lineNumbers | booleanNumbers each line. | false |
highlightLines | number[]Lines to mark, from 1. | — |
wrap | booleanLong lines wrap instead of scrolling sideways. | false |
maxHeight | number | stringScrolls past this height. | — |
copyable | booleanThe copy button. | true |
children | ReactNodeYour highlighter's markup in place of the plain lines; code is still what's copied. | — |
classNames | { header, pre, line }Classes for each part. | — |
className | stringExtra classes, merged with cn() so yours win. | — |
Keyboard
| Keys | Action |
|---|---|
| Tab | To the code (it scrolls with the arrow keys), the tabs and the copy button |
| ←or→ | Between tabs |
| HomeorEnd | The first or last tab |
Accessibility
- The tabs follow the tabs pattern (tablist, tab, tabpanel, aria-selected).
- Line numbers are hidden from screen readers and from selection, so they're never copied.
- Code runs left to right on an Arabic page too.