Code Block
React data-displayRead-only code preview with a header, optional filename, copy button, and `<pre>`-rendered content. Use for installation snippets, API examples, and snippets you want users to copy verbatim.
Also available for Vue ->Installation
$ pnpm dlx shadcn@latest add https://react.uipkge.dev/r/react/code-block.json$ npx shadcn@latest add https://react.uipkge.dev/r/react/code-block.json$ yarn dlx shadcn@latest add https://react.uipkge.dev/r/react/code-block.json$ bunx shadcn@latest add https://react.uipkge.dev/r/react/code-block.json
Or with the named registry:
npx shadcn@latest add @uipkge-react/code-block
Examples
Props
| Name | Type / Values | Default | Required |
|---|---|---|---|
code Source code to display. | string | — | required |
language Language label shown in the header. | string | — | optional |
showLineNumbers Render line numbers in the gutter. | boolean | — | optional |
maxHeight Maximum height of the code body before scrolling kicks in. | string | — | optional |
defaultExpanded Render expanded on first paint. Default true. | boolean | — | optional |
showHeader Show the language label / copy / collapse header. | boolean | — | optional |
className | string | — | optional |
Dependencies
Files (2)
-
components/ui/code-block/CodeBlock.tsx 4 kB
'use client' import * as React from 'react' import { Check, ChevronDown, ChevronUp, Copy } from 'lucide-react' import { cn } from '@/lib/utils' import { Button } from '@/components/ui/button' export interface CodeBlockProps { /** Source code to display. */ code: string /** Language label shown in the header. */ language?: string /** Render line numbers in the gutter. */ showLineNumbers?: boolean /** Maximum height of the code body before scrolling kicks in. */ maxHeight?: string /** Render expanded on first paint. Default true. */ defaultExpanded?: boolean /** Show the language label / copy / collapse header. */ showHeader?: boolean className?: string } function CodeBlock({ code, language = 'tsx', showLineNumbers = true, maxHeight = '400px', defaultExpanded = true, showHeader = true, className, }: CodeBlockProps) { const [isExpanded, setIsExpanded] = React.useState(defaultExpanded) const [copied, setCopied] = React.useState(false) const lines = React.useMemo(() => code.replace(/\s+$/, '').split('\n'), [code]) async function copyToClipboard() { try { await navigator.clipboard.writeText(code) setCopied(true) setTimeout(() => setCopied(false), 1600) } catch (e) { console.warn('Clipboard write failed', e) } } return ( <div data-uipkge="" data-slot="code-block" className={cn('group border-border bg-muted/20 relative overflow-hidden rounded-lg border', className)} > {/* Header */} {showHeader && ( <div className="border-border bg-muted/40 flex items-center justify-between gap-2 border-b px-3 py-2"> <span className="text-muted-foreground text-xs font-medium tracking-wide uppercase">{language}</span> <div className="flex items-center gap-1"> <Button variant="ghost" size="xs" className="h-7 gap-1.5 px-2" onClick={copyToClipboard}> {copied ? ( <Check className="text-success size-3" aria-hidden="true" /> ) : ( <Copy className="size-3" aria-hidden="true" /> )} <span className="text-xs">{copied ? 'Copied' : 'Copy'}</span> </Button> <Button variant="ghost" size="xs" className="h-7 gap-1.5 px-2" onClick={() => setIsExpanded((v) => !v)} > {isExpanded ? ( <ChevronUp className="size-3" aria-hidden="true" /> ) : ( <ChevronDown className="size-3" aria-hidden="true" /> )} <span className="text-xs">{isExpanded ? 'Hide' : 'Show'} code</span> </Button> </div> </div> )} {/* Code body. We render line-by-line so the gutter and the code stay in sync without measuring text height. The renderer is intentionally plain: code is displayed as-is and the consumer can layer their own syntax highlighter (shiki, prism, hljs) on top by replacing this file or by wrapping it in their own component. */} {isExpanded && ( <div className="bg-background/40 overflow-auto font-mono text-sm leading-relaxed" style={{ maxHeight }} > <pre className="m-0 flex"> {showLineNumbers && ( <div aria-hidden="true" className="text-muted-foreground/60 sticky left-0 select-none border-r border-border/60 bg-muted/30 px-3 py-3 text-right tabular-nums" > {lines.map((_, i) => ( <div key={i}>{i + 1}</div> ))} </div> )} <code className="block flex-1 px-4 py-3"> {lines.map((line, i) => ( <React.Fragment key={i}> <span>{line || ' '}</span> {i < lines.length - 1 && <br />} </React.Fragment> ))} </code> </pre> </div> )} </div> ) } export { CodeBlock } -
components/ui/code-block/index.ts 0.1 kB
export { CodeBlock, type CodeBlockProps } from './CodeBlock'
Raw manifest: https://react.uipkge.dev/r/react/code-block.json