Code Block

Syntax-highlighted code display with copy support. Install Code Block from the VLLNT UI registry with the shadcn CLI.

Report a bug

Syntax-highlighted code display with copy support. Part of the content family in VLLNT UI, it ships as a machine-readable registry entry — copy the source directly into your app with the shadcn CLI and own it, no runtime dependency on a component library.

Preview

Switch between light and dark to inspect the embedded Storybook preview.

Installation

Add Code Block to your project with the shadcn CLI. The source lands in your codebase, ready to adapt:

pnpm dlx shadcn@latest add https://ui.vllnt.ai/r/code-block.json

Source

"use client"; import { type ComponentType, type ReactNode, useEffect, useRef, useState, } from "react"; import { Check, Copy } from "lucide-react"; import { useTheme } from "next-themes"; import type { SyntaxHighlighterProps } from "react-syntax-highlighter"; import { cn } from "../../lib/utils"; import { Button } from "../button/button"; import { useCopyToClipboard } from "../copy-button/copy-button"; type PrismStyle = SyntaxHighlighterProps["style"]; type LoadedHighlighter = { oneDark: PrismStyle; oneLight: PrismStyle; SyntaxHighlighter: ComponentType<SyntaxHighlighterProps>; }; type CodeBlockProps = { children: ReactNode; className?: string; language?: string; showLanguage?: boolean; }; function extractTextFromChildren(node: ReactNode): string { if (typeof node === "string") { return node; } if (typeof node === "number") { return String(node); } if (Array.isArray(node)) { return node.map(extractTextFromChildren).join(""); } if ( node && typeof node === "object" && "props" in node && node.props && typeof node.props === "object" && "children" in node.props ) { return extractTextFromChildren(node.props.children as ReactNode); } return String(node ?? ""); } function findScrollableParent( element: HTMLElement | null, ): HTMLElement | undefined { if (!element) return undefined; if (element.scrollHeight > element.clientHeight) return element; return findScrollableParent(element.parentElement); } export function CodeBlock({ children, className, language = "typescript", showLanguage = false, }: CodeBlockProps) { const { copied, copy } = useCopyToClipboard(); // react-syntax-highlighter (~10MB) is dynamic-imported on mount so the // @vllnt/ui barrel's static graph never reaches it — barrel consumers that // never render a CodeBlock ship zero bytes of it. Null until the chunk loads. const [highlighter, setHighlighter] = useState<LoadedHighlighter | null>( null, ); const { systemTheme, theme } = useTheme(); const resolvedTheme = theme === "system" ? systemTheme : theme; const isDark = resolvedTheme !== "light"; const code = extractTextFromChildren(children); const scrollRef = useRef<HTMLDivElement>(null); useEffect(() => { let active = true; void Promise.all([ import("react-syntax-highlighter"), import("react-syntax-highlighter/dist/esm/styles/prism"), ]).then(([module_, styles]) => { if (!active) return; setHighlighter({ oneDark: styles.oneDark, oneLight: styles.oneLight, SyntaxHighlighter: module_.Prism, }); }); return () => { active = false; }; }, []); useEffect(() => { const element = scrollRef.current; if (!element) return; const onWheel = (event: WheelEvent) => { if (Math.abs(event.deltaY) <= Math.abs(event.deltaX)) return; const scrollable = findScrollableParent(element); if (scrollable) { scrollable.scrollTop += event.deltaY; event.preventDefault(); } }; element.addEventListener("wheel", onWheel, { passive: false }); return () => { element.removeEventListener("wheel", onWheel); }; }, []); const handleCopy = () => { void copy(code); }; const SyntaxHighlighter = highlighter?.SyntaxHighlighter; const codeStyle = isDark ? highlighter?.oneDark : highlighter?.oneLight; return ( <div className={cn( "relative w-full overflow-hidden rounded-md border bg-background", className, )} > <div className="relative overflow-x-auto overflow-y-hidden touch-pan-y" ref={scrollRef} > {SyntaxHighlighter ? ( <SyntaxHighlighter codeTagProps={{ className: "font-mono text-sm", style: { background: "transparent", display: "block", }, }} customStyle={{ background: "oklch(var(--background))", fontSize: "0.875rem", margin: 0, minWidth: "fit-content", overflowY: "hidden", padding: "1rem", }} language={language} style={codeStyle} > {code} </SyntaxHighlighter> ) : ( <pre className="m-0 min-w-fit overflow-y-hidden p-4 font-mono text-sm"> <code className="block bg-transparent">{code}</code> </pre> )} <div className="absolute right-2 top-2 flex items-center gap-2"> {showLanguage ? ( <span className="text-xs font-mono text-muted-foreground uppercase tracking-wider"> {language} </span> ) : null} <Button aria-label={copied ? "Copied" : "Copy code"} className="size-8" onClick={handleCopy} size="icon" variant="ghost" > {copied ? ( <Check className="size-3" /> ) : ( <Copy className="size-3" /> )} </Button> </div> </div> </div> ); }

Stories

Explore every variant and state in the interactive Storybook:

Preview

Switch between light and dark to inspect the embedded Storybook preview.

Dependencies

  • @vllnt/ui@^0.3.0