Mischief

41 / Code

Code Block

A code panel with copy, optional line numbers, highlighted lines, and a collapse for anything long.

route.ts
import { rateLimit } from "./limiter" export async function POST(request: Request) {  const allowed = await rateLimit(request)   if (!allowed) {    return Response.json({ error: "Slow down" }, { status: 429 })  }   const body = await request.json()  return Response.json({ ok: true, body })}

Installation

Copy the source into your project, or keep it behind a package.

npx shadcn@latest add Tinkerers-Labs/mischief-ui/code-block
import { CodeBlock } from "mischief-ui/code-block"
Also installs
  • lucide-react

Or paste it in yourself. The source imports the shared cn helper from @/lib/utils, so point that at your own copy.

registry/default/code-block/code-block.tsx
"use client" import * as React from "react"import { Check, ChevronDown, Clipboard, WrapText } from "lucide-react" import { cn } from "@/lib/utils" export type CodeBlockProps = Omit<  React.HTMLAttributes<HTMLDivElement>,  "children"> & {  code: string  /** Shown in the header, and used as the download-free label for the block. */  filename?: string

Usage

export function Snippet() {
  return (
    <CodeBlock
      code={source}
      filename="server.ts"
      showLineNumbers
      highlightLines={[4, 5]}
      maxLines={12}
    />
  )
}

On syntax highlighting

There is none, and that is deliberate. Every highlighter worth using is larger than this entire library, and one baked in would be paid for by everyone who only wanted a copy button. The component is a place to put code, not an opinion about how code should be coloured.

When you do want it, highlight on the server and pass the result as children of your own pre, or reach for shiki or Prism directly. The language prop is a label rather than an instruction -- nothing reads it but the header.

Long code

maxLines collapses anything past a point behind a toggle that says how much is hidden, and the copy button always copies the whole source rather than the visible part. Combine it with wrappable when lines are long as well as many, so the reader can choose between scrolling sideways and reading wrapped.

<CodeBlock
  code={source}
  filename="server.ts"
  showLineNumbers
  highlightLines={[12, 13]}
  maxLines={20}
  wrappable
/>
highlightLines is one-based, matching the gutter.

API

codestringThe source to render. A single trailing newline is dropped.
filenamestringShown in the header, and preferred over language.
languagestringA short label such as tsx, used when there is no filename.
showLineNumbersbooleanAdds a gutter sized to the highest line number.
highlightLinesnumber[]One-based lines to mark as the interesting ones.
maxLinesnumberCollapses anything past this many lines behind a toggle.
wrap, wrappableboolean, booleanWrap long lines, and offer a control that overrides it.
copyablebooleanShows the copy control. Defaults to true.
actionsReactNodeExtra controls placed in the header.

Accessibility

The code region is focusable so it can be scrolled from the keyboard. Copying announces itself through a polite live region, and the copy control renames itself once it succeeds. A clipboard that refuses -- denied permission, an insecure context, a sandboxed frame -- is caught and reported rather than leaving the control looking like it worked. Line numbers and the diff-style gutter are aria-hidden, so a screen reader reads the source rather than the decoration. There is no syntax highlighting and no highlighting dependency.