Skip to content
TopClass UI

Code block

Code in a box of its own, read left to right on any page. The block renders on the server; the copy button is its only script.

split - size not measured yet

Code block

split - size not measured yet

Code in a box of its own, read left to right on any page. The block renders on the server; the copy button is its only script.

Install
app/invoices/page.tsx
export default async function Page() {
  const invoices = await getInvoices({ status: 'overdue', sortBy: 'dueDate', order: 'descending' })
  return <InvoiceTable items={invoices} />
}

Try it

showCopy
wrap

The code

code-block.tsx
import type { CodeBlockDesignProps } from 'topclassui/code-block'
import { CodeBlock } from 'topclassui/code-block'

/** The docs stage: the Try it controls hand their values in as props. One line is long, so wrap shows. */
export default function Demo(props: CodeBlockDesignProps) {
  return (
    <CodeBlock lang="tsx" title="app/invoices/page.tsx" {...props}>
      {`export default async function Page() {
  const invoices = await getInvoices({ status: 'overdue', sortBy: 'dueDate', order: 'descending' })
  return <InvoiceTable items={invoices} />
}`}
    </CodeBlock>
  )
}

Pass the code as a template-literal child. No highlighter is built in: pass coloured nodes as highlighted.

Every state

3 states
app/page.tsx
export default async function Page() {
  const invoices = await getInvoices()
  return <InvoiceTable items={invoices} />
}
Idle
columns.ts
export const columns = ['number', 'customer', 'issued', 'due', 'amount', 'status', 'actions'] as const
Wrapped
app/reports/page.tsx
export default async function Page() {
  const invoices = await getInvoices()
  return <InvoiceTable items={invoices} />
}
No copy button

Import from the package

topclassui/code-block
import { CodeBlock } from 'topclassui/code-block'

Install once:npm i topclassui@beta zod

Examples

  • The code is children, a string: every space and line break is kept, and that string is what the button copies.
  • title names the block: the code becomes a region named by it, and the button reads "Copy page.tsx".
  • lang is written as data-lang and nothing else. To colour the code, make the nodes yourself, at build time or in your own Server Component, and pass them as highlighted; the button still copies children.
  • A long line scrolls inside the block; wrap breaks it instead.
  • showCopy={false} drops the button, and the block then ships no JavaScript at all.
  • In a Server Component pass {...getLabels(locale)}: the button's words come from labels.common.
basic
page.tsx
export default async function Page() {
  const invoices = await getInvoices()
  return <InvoiceTable items={invoices} />
}
highlighted
sample.tsx
import { CodeBlock } from 'topclassui/code-block'

// Coloured by the caller, copied as plain text
export const sample = <CodeBlock title="page.tsx">{code}</CodeBlock>
long-line
token.ts
const token = 'a1b2c3d4e5a1b2c3d4e5a1b2c3d4e5a1b2c3d4e5a1b2c3d4e5a1b2c3d4e5a1b2c3d4e5a1b2c3d4e5a1b2c3d4e5a1b2c3d4e5a1b2c3d4e5a1b2c3d4e5a1b2c3d4e5a1b2c3d4e5a1b2c3d4e5a1b2c3d4e5a1b2c3d4e5a1b2c3d4e5a1b2c3d4e5a1b2c3d4e5a1b2c3d4e5a1b2c3d4e5a1b2c3d4e5a1b2c3d4e5a1b2c3d4e5a1b2c3d4e5a1b2c3d4e5a1b2c3d4e5abcd'
wrap
invoice-row.tsx
export const columns = ['number', 'customer', 'issued', 'due', 'amount', 'status', 'actions'] as const satisfies readonly InvoiceColumn[]
no-copy
globals.css
:root {
  --tc-radius: 0.75rem;
}
no-header
pnpm add topclassui zod
In Arabic, right to left
الفواتير.tsx
export default async function Page() {
  const invoices = await getInvoices()
  return <InvoiceTable items={invoices} />
}

Props

The props of CodeBlock
PropTypeDefaultLimitsDescription
children*string--The code, as a template literal: every space and line break is kept, and this is what the button copies.
highlightedReactNode--The same code as nodes the caller coloured, for example with a highlighter at build time. Rendered in place of children.
labelsLabels--The built-in strings of the copy button. Defaults to English; pass getLabels(locale).labels for another.
langstring--The language, written as data-lang for a stylesheet or a script to read. Nothing here colours it.
localestring--Accepted so {...getLabels(locale)} spreads cleanly. Nothing here formats a number, so it is not used and never reaches the element.
showCopybooleantrue-Shows a button in the header that copies the code: the one part that runs in the browser.
titleReactNode--The file name in the header. It names the code region and the copy button.
wrapbooleanfalse-Breaks a long line instead of scrolling it sideways; the code is then no tab stop.

Keyboard and accessibility

The keyboard of CodeBlock
KeysWhat it does
TabMoves to the copy button, then to the code, which is a tab stop so a keyboard can scroll it.
EnterSpaceOn the copy button, copies the code; a hidden status says "Copied" once.
ArrowLeftArrowRightOn the code, scrolls a long line sideways. With wrap nothing scrolls and the code is no tab stop.
  • The button's name never changes: the tick is the cue to the eye, and a polite status says "Copied" or "Could not copy" once per press.
  • Copying needs a secure context (https or localhost); elsewhere the button says "Could not copy" and keeps its icon.
  • Colours you pass in highlighted are yours to check against the block's surface.

Right to left

The code is always dir="ltr" and translate="no". The header keeps the page's direction, so in Arabic the copy button sits on the left, at the inline end.

Server and client

Renders on the server; the interactive part is a client component that ships only when it is used.

Files: index.tsx, client.tsx.

On the chip: split - size not measured yet.