CodeBlock

A code block highlighted on the server, with a copy button.

Import

Highlighting happens on the server, and shiki never reaches the browser. It imports server-only, so importing it from a Client Component fails the build, which is why it has an entry of its own.

ts
import { CodeBlock } from '@k8ordo/ui/code-block';

Usage

tsx
export function Total({ items }: { items: Item[] }) {
  const done = items.filter((item) => item.done).length;
  return <p>{done} / {items.length}</p>;
}
tsx
<CodeBlock code={source} lang="tsx" />

File Name

With title, the header shows it in place of the language, as the figure’s figcaption.

total.tsx
export function Total({ items }: { items: Item[] }) {
  const done = items.filter((item) => item.done).length;
  return <p>{done} / {items.length}</p>;
}
tsx
<CodeBlock code={source} lang="tsx" title="total.tsx" />

Line Marks

marks marks lines by their 1-based number with highlight, add, or remove. Additions and removals are shown with + and −, not by color alone.

ts
const done = items.filter((item) => item.done).length;
const rate = done / items.length;
const rate = items.length === 0 ? 0 : done / items.length;
tsx
<CodeBlock
  code={source}
  lang="ts"
  marks={{ 1: 'highlight', 2: 'remove', 3: 'add' }}
/>

Callouts

callouts puts a note right under a line, indented like the line (an array puts several, in order). The copy button copies code exactly, without the marks or the notes.

ts
const done = items.filter((item) => item.done).length;
const rate = done / items.length;
const rate = items.length === 0 ? 0 : done / items.length;
Guard the division when the list is empty
tsx
<CodeBlock
  callouts={{ 3: 'Guard the division when the list is empty' }}
  code={source}
  lang="ts"
/>

Colors and Dark Mode

It uses shiki’s css-variables theme, with each --shiki-token-* mapped to a design token. The tokens switch under .dark, so there is no second theme. A language name shiki does not know renders as plain text.

Props

code
Type: string
Default: -
callouts?
Type: Readonly<Record<number, string | readonly string[]>>
Default: -
lang?
Type: string
Default: 'text'
marks?
Type: Readonly<Record<number, 'highlight' | 'add' | 'remove'>>
Default: -
title?
Type: string
Default: -

Type base (some attrs are managed internally): HTMLAttributes<HTMLElement>

Wording this component renders (labels, placeholders, and the like) comes from the message dictionary when no prop sets it. To change it, see: i18n