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.json

Usage

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

  1. Shiki on the client, with plaintext fallback. codeToHtml runs inside useEffect. Until it resolves, the raw code renders 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 on window at module scope.
  2. Self-contained theme. A neutral light theme (code-block.theme.json) ships alongside the component as the default. Pass any shiki ThemeRegistration via theme to override. The registry-installed copy works standalone — there's no implicit dependency on the docs site's theme files.
  3. 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 a cb-codeblock-line-hl class to lines in highlightLines. 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.
  4. Built-in copy button. A small inline button (no docs-only CopyButton import) is absolutely-positioned top-right, writes code to the clipboard via navigator.clipboard, toggles a check for 1.8 seconds via a guarded setTimeout, then resets on unmount through the effect's cleanup.
  5. 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

PropTypeDefaultDescription
codestringrequiredThe code to render.
lang'tsx' | 'ts' | 'jsx' | 'js' | 'bash' | 'css' | 'json' | 'html' | 'md''tsx'Shiki language id.
titlestringFilename or label rendered in a title bar above the code.
showLineNumbersbooleanfalseRender a 1-indexed line-number gutter.
highlightLinesreadonly number[][]1-indexed lines to tint with cb-accent-muted.
themeThemeRegistrationneutral lightShiki theme — defaults to a self-contained neutral theme.
classNamestringMerged onto the rendered <div>.

Accessibility

  • The copy button has an aria-label that 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.