Code Block
A library-grade code block: shiki-powered syntax highlighting, an optional filename title bar, optional line numbers, optional line-by-line emphasis, and an always-visible copy button in the top-right.
Preview
import { Button } from "@craft-bits/core";
export function Save() {
return (
<Button tone="accent" onClick={() => alert("saved")}>
Save
</Button>
);
}Customize
Language
tsx
Features
Installation
npx shadcn@latest add https://craftbits.dev/r/code-block.jsonUsage
import { CodeBlock } from "@craft-bits/core";
<CodeBlock code={`const x = 1;`} lang="ts" />With a title bar, line numbers, and an emphasized line:
<CodeBlock
title="button-save.tsx"
code={source}
lang="tsx"
showLineNumbers
highlightLines={[4, 5, 6]}
/>Understanding the component
- Shiki on the client, with plaintext fallback.
codeToHtmlruns insideuseEffect. Until it resolves, the rawcoderenders inside a<pre><code>using the same monospace stack and padding as the highlighted output — there's no layout shift on swap, and SSR is clean because nothing depends onwindowat module scope. - Self-contained theme. A neutral light theme (
code-block.theme.json) ships alongside the component as the default. Pass any shikiThemeRegistrationviathemeto override. The registry-installed copy works standalone — there's no implicit dependency on the docs site's theme files. - Line decoration via string-splice. Shiki emits each line as
<span class="line">…</span>. A regex-based pass inserts a<span class="cb-codeblock-ln">N</span>gutter into each line and adds acb-codeblock-line-hlclass to lines inhighlightLines. Tailwind arbitrary-variant selectors ([&_.cb-codeblock-line-hl]:bg-cb-accent-muted/70) drive the styling — there are no per-line React renders, so a 500-line file pays for one mount. - Built-in copy button. A small inline button (no docs-only
CopyButtonimport) is absolutely-positioned top-right, writescodeto the clipboard vianavigator.clipboard, toggles a check for 1.8 seconds via a guardedsetTimeout, then resets on unmount through the effect's cleanup. - Token-driven palette. Surface, border, and gutter colors come from
bg-cb-bg-muted,border-cb-border-muted,--cb-fg-subtle. Re-skin the entire library — including the code block — by overriding the--cb-*vars at the app root.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
code | string | required | The code to render. |
lang | 'tsx' | 'ts' | 'jsx' | 'js' | 'bash' | 'css' | 'json' | 'html' | 'md' | 'tsx' | Shiki language id. |
title | string | — | Filename or label rendered in a title bar above the code. |
showLineNumbers | boolean | false | Render a 1-indexed line-number gutter. |
highlightLines | readonly number[] | [] | 1-indexed lines to tint with cb-accent-muted. |
theme | ThemeRegistration | neutral light | Shiki theme — defaults to a self-contained neutral theme. |
className | string | — | Merged onto the rendered <div>. |
Accessibility
- The copy button has an
aria-labelthat switches between"Copy code"and"Copied"so screen readers can announce the success state. - Line-number gutter spans are
aria-hidden="true"— only the visible code is read by assistive tech. - Highlighted lines are conveyed by background tint only, so consumers should additionally surface the highlight semantically (e.g. with adjacent prose) when the meaning matters for screen readers.
- The container is a plain
<div>containing a<pre>— keyboard users can scroll horizontally with arrow keys when a long line overflows.
Credits
- Extracted from:
AlgoFlashcards(src/platform/ui/CodeBlock.tsx). The library version is a re-architecture: title bar, line numbers, line highlighting, and a built-in copy button were added.