Code Block
Shiki highlighting, line numbers, copy, and scroll area for long snippets. Not tied to Markdown.
Plus Registry
This component ships from registry.plus.json, not the public registry.json. Set up @f-ui-plus and a token on the Installation page, then install @f-ui-plus/code-block below.
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, and a scrollable body for long files. Source stays visible as plain monospace until highlight is ready (plain-first); pass loading only to force a spinner for demos. Use it anywhere you need a polished code viewer; markdown-renderer can compose it for fenced blocks.
Scroll / copy layout (Chakra CodeBlock / GitHub-class): copy chrome lives outside the overflow-auto scrollport (header when chrome, absolute overlay when minimal). One scrollport owns both axes so the horizontal scrollbar stays pinned while you scroll vertically; sticky gutters pin on that same scrollport. Path titles truncate the directory and keep the basename; an optional file-type icon sits left of the label.
For rendered diagram previews (image view, zoom, pan, export), use the dedicated Mermaid Renderer component—not CodeBlock alone.
Installing
Configure @f-ui-plus and FUI_PLUS_REGISTRY_TOKEN as in Installation — Plus Registry.
FUI_PLUS_REGISTRY_TOKEN=xxx pnpm dlx shadcn@latest add @f-ui-plus/code-blockFUI_PLUS_REGISTRY_TOKEN=xxx npx shadcn@latest add @f-ui-plus/code-blockFUI_PLUS_REGISTRY_TOKEN=xxx yarn dlx shadcn@latest add @f-ui-plus/code-blockFUI_PLUS_REGISTRY_TOKEN=xxx bun x shadcn@latest add @f-ui-plus/code-blockregistryDependencies: copy-affordance, file-type-icon. Runtime: shiki, next-themes (for light/dark theme alignment).
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"
/>
);
}Examples
Minimal (Default)
"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
Long path titles truncate the directory with an ellipsis while preferring the basename; when the name alone is too long it can ellipsis too. An optional file-type icon appears left of the label when a title is set.
"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="examples/powerapp/web/src/server.ts"
chrome
lineNumbers
copyStyle="always"
/>
);
}Long Lines (Sticky Gutter on Horizontal Scroll)
"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
"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 (Forced Spinner)
The demo below sets loading to force the spinner (demos only). Normal usage keeps plain source visible until highlight is ready.
Pass loading to show the same spinner the component uses while Shiki initializes—no duplicate markup.
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.
"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 Reference
| Prop | Type | Description |
|---|---|---|
source | string | Raw source (copied on Copy). |
language | string | Shiki language id or alias (rust, c++, go, …). Warm-loads mainstream languages; any other Shiki bundled grammar loads on demand. Omit for plain monospace. |
title | string | Header label when chrome is on; overrides language in the header. |
showFileIcon | boolean | Chrome only. Default on when title is set; off for language-only chrome. Uses File Type Icon. |
chrome | boolean | Show title bar (default false). |
lineNumbers | boolean | Show gutter (default false). |
lineStart | number | First line number (default 1). |
wordWrap | boolean | Soft-wrap long lines (default false). |
showCopy | boolean | Show copy control (default true). |
copyStyle | "hover" | "always" | Minimal (no chrome) overlay visibility (default hover). With chrome, copy always sits in the non-scrolling header. |
maxHeightClassName | string | Tailwind class for scroll region height (default max-h-[min(70vh,28rem)]). |
className | string | Outer container. |
classNames | CodeBlockClassNames | Optional slots: root, header, titleRow, scroll, body, pre. |
loading | boolean | If true, forces a spinner and hides source (demos only). |
While Shiki (or an on-demand grammar) is warming, the block shows plain monospace source — not a spinner — then swaps to highlighted HTML once ready.