f-ui
Components

Code Block

Shiki highlighting, line numbers, copy, and scroll area for long snippets. Not tied to Markdown.

Private Registry

This component is distributed only from registry.private.json (Bearer token). It is not in the public registry.json. Configure @f-ui-private as in the data table docs, then install with @f-ui-private/code-block.

The code-block registry item provides CodeBlock: syntax highlighting (Shiki), optional chrome (title bar), line numbers with a sticky gutter on horizontal scroll, optional word wrap, copy, a spinner loading state while Shiki initializes, and a scrollable body for long files. Use it anywhere you need a polished code viewer; markdown-renderer can compose it for fenced blocks.

For rendered diagram previews (image view, zoom, pan, export), use the dedicated Mermaid Renderer component—not CodeBlock alone.

Installation

1. Configure components.json

Add @f-ui-private (same as Data Table).

2. Install

REGISTRY_TOKEN=xxx pnpm dlx shadcn@latest add @f-ui-private/code-block
REGISTRY_TOKEN=xxx npx shadcn@latest add @f-ui-private/code-block
REGISTRY_TOKEN=xxx yarn dlx shadcn@latest add @f-ui-private/code-block
REGISTRY_TOKEN=xxx bun x shadcn@latest add @f-ui-private/code-block

registryDependencies: none. Runtime: shiki, next-themes (for light/dark theme alignment).

Contributors

Local build: pnpm registry:build:private — install from .registry-private/r/code-block.json.

Usage

import { CodeBlock } from "@/components/f-ui/code-block/code-block";

export function Example() {
  return (
    <CodeBlock
      source={'console.log("hi")'}
      language="ts"
      title="example.ts"
    />
  );
}

Demos

Minimal (Default)

Loading syntax highlight
"use client";

import { CodeBlock } from "@/components/f-ui/code-block/code-block";

import { HONO_SAMPLE } from "@/demos/code-block/samples";

export function CodeBlockMinimalDemo() {
  return <CodeBlock source={HONO_SAMPLE} language="ts" />;
}

Chrome + Line Numbers

server.ts
Loading syntax highlight
"use client";

import { CodeBlock } from "@/components/f-ui/code-block/code-block";

import { HONO_SAMPLE } from "@/demos/code-block/samples";

export function CodeBlockChromeLineNumbersDemo() {
  return (
    <CodeBlock
      source={HONO_SAMPLE}
      language="ts"
      title="server.ts"
      chrome
      lineNumbers
      copyStyle="always"
    />
  );
}

Long Lines (Sticky Gutter on Horizontal Scroll)

router.ts
Loading syntax highlight
"use client";

import { CodeBlock } from "@/components/f-ui/code-block/code-block";

import { LONG_LINE_ROUTER_SAMPLE } from "@/demos/code-block/samples";

/** Long lines: sticky line-number gutter while code scrolls horizontally. */
export function CodeBlockStickyGutterDemo() {
  return (
    <CodeBlock
      source={LONG_LINE_ROUTER_SAMPLE}
      language="ts"
      title="router.ts"
      chrome
      lineNumbers
      copyStyle="always"
    />
  );
}

Word Wrap + Line Numbers

router.ts
Loading syntax highlight
"use client";

import { CodeBlock } from "@/components/f-ui/code-block/code-block";

import { LONG_LINE_ROUTER_SAMPLE } from "@/demos/code-block/samples";

export function CodeBlockWordWrapDemo() {
  return (
    <CodeBlock
      source={LONG_LINE_ROUTER_SAMPLE}
      language="ts"
      title="router.ts"
      chrome
      lineNumbers
      wordWrap
      copyStyle="always"
    />
  );
}

Loading (Shiki Initializing)

The demo below sets loading so the spinner is visible without reimplementing the component shell.

Pass loading to show the same spinner the component uses while Shiki initializes—no duplicate markup.

example.ts
Loading syntax highlight
import { CodeBlock } from "@/components/f-ui/code-block/code-block"

const SOURCE = 'console.log("hello")'

/** Uses `loading` so the doc demo does not duplicate CodeBlock internals. */
export function CodeBlockLoadingDemo() {
  return (
    <div className="space-y-3">
      <p className="text-muted-foreground text-sm">
        Pass <code className="rounded bg-muted px-1 py-0.5 font-mono text-xs">loading</code> to
        show the same spinner the component uses while Shiki initializes—no duplicate markup.
      </p>
      <CodeBlock
        source={SOURCE}
        language="ts"
        title="example.ts"
        chrome
        loading
      />
    </div>
  )
}

Mermaid (Highlighted Source)

CodeBlock with language="mermaid" behaves like any other language: Shiki highlighting, copy, scroll, and optional chrome or line numbers.

Preview vs. Source Only

This shows syntax-highlighted Mermaid text only. For an interactive diagram preview (Image/Code toggle, toolbar, fullscreen), use Mermaid Renderer.

Loading syntax highlight
"use client";

import { CodeBlock } from "@/components/f-ui/code-block/code-block";

import { MERMAID_SAMPLE } from "@/demos/code-block/samples";

export function CodeBlockMermaidDemo() {
  return <CodeBlock source={MERMAID_SAMPLE} language="mermaid" />;
}

API

PropTypeDescription
sourcestringRaw source (copied on Copy).
languagestringShiki language id; omit for plain monospace.
titlestringHeader label when chrome is on; overrides language in the header.
chromebooleanShow title bar (default false).
lineNumbersbooleanShow gutter (default false).
lineStartnumberFirst line number (default 1).
wordWrapbooleanSoft-wrap long lines (default false).
showCopybooleanShow copy control (default true).
copyStyle"hover" | "always"Copy placement (default hover).
maxHeightClassNamestringTailwind class for scroll region height (default max-h-[min(70vh,28rem)]).
classNamestringOuter container.
classNamesCodeBlockClassNamesOptional slots: root, header, titleRow, scroll, body, pre.
loadingbooleanIf true, forces the Shiki loading UI (spinner). For demos or stories; normally omit and let the block load Shiki on its own.

When language is set and Shiki is still loading, the block shows a centered spinner only (screen-reader text for status)—no visible label and no unstyled source text.

On this page