Code Block

A syntax-highlighted code block with copy functionality and language support.

example.tsx
import { Button } from "@/components/ui/button"
export function Example() {
return <Button>Click me</Button>
}
tsx
import { CodeBlock } from "@/components/ui/code-block"
export function CodeBlockDemo() {
return (
<CodeBlock
language="tsx"
filename="example.tsx"
code={`import { Button } from "@/components/ui/button"
export function Example() {
return <Button>Click me</Button>
}`}
/>
)
}

Installation

CLI

bash
npx fivui add code-block

Manual

Copy and paste the following code into your project.

bash
npm install prism-react-renderer lucide-react
components/ui/code-block.tsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
"use client"
import * as React from "react"
import { Check, Copy, FileText, Terminal } from "lucide-react"
import { Highlight, themes } from "prism-react-renderer"
import { cva, type VariantProps } from "class-variance-authority"
import { cn } from "@/lib/utils"
import { Button } from "@/components/ui/button"
const codeBlockVariants = cva(
"relative overflow-hidden rounded-lg border bg-muted/50",
{
variants: {
variant: {
default: "border-border",
ghost: "border-transparent bg-transparent",
},
},
defaultVariants: {
variant: "default",
},
}
)
interface CodeBlockProps
extends React.HTMLAttributes<HTMLDivElement>,
VariantProps<typeof codeBlockVariants> {
code: string
language?: string
filename?: string
showLineNumbers?: boolean
showCopy?: boolean
maxHeight?: string
theme?: "light" | "dark"
}
// Language icons mapping
const languageIcons: Record<string, React.ReactNode> = {
javascript: <FileText className="h-4 w-4 text-yellow-500" />,
typescript: <FileText className="h-4 w-4 text-blue-500" />,
jsx: <FileText className="h-4 w-4 text-cyan-500" />,
tsx: <FileText className="h-4 w-4 text-blue-600" />,
python: <FileText className="h-4 w-4 text-green-500" />,
bash: <Terminal className="h-4 w-4 text-gray-500" />,
shell: <Terminal className="h-4 w-4 text-gray-500" />,
json: <FileText className="h-4 w-4 text-orange-500" />,
css: <FileText className="h-4 w-4 text-purple-500" />,
html: <FileText className="h-4 w-4 text-red-500" />,
sql: <FileText className="h-4 w-4 text-blue-400" />,
yaml: <FileText className="h-4 w-4 text-red-400" />,
xml: <FileText className="h-4 w-4 text-orange-400" />,
default: <FileText className="h-4 w-4 text-muted-foreground" />,
}
const CodeBlock = React.forwardRef<HTMLDivElement, CodeBlockProps>(
({
className,
variant,
code,
language = "text",
filename,
showLineNumbers = false,
showCopy = true,
maxHeight = "400px",
theme = "dark",
...props
}, ref) => {
const [copied, setCopied] = React.useState(false)
const copyToClipboard = async () => {
try {
await navigator.clipboard.writeText(code)
setCopied(true)
setTimeout(() => setCopied(false), 2000)
} catch (err) {
console.error("Failed to copy code:", err)
}
}
const languageIcon = languageIcons[language.toLowerCase()] || languageIcons.default
return (
<div
ref={ref}
className={cn(codeBlockVariants({ variant, className }))}
{...props}
>
{/* Header */}
{(filename || language || showCopy) && (
<div className="flex items-center justify-between border-b bg-muted/30 px-4 py-2">
<div className="flex items-center gap-2">
{languageIcon}
{filename && (
<span className="text-sm font-medium text-foreground">
{filename}
</span>
)}
{!filename && language && language !== "text" && (
<span className="text-sm text-muted-foreground">
{language}
</span>
)}
</div>
{showCopy && (
<Button
variant="ghost"
size="sm"
className="h-8 w-8 p-0"
onClick={copyToClipboard}
>
{copied ? (
<Check className="h-4 w-4" />
) : (
<Copy className="h-4 w-4" />
)}
<span className="sr-only">Copy code</span>
</Button>
)}
</div>
)}
{/* Code Content */}
<div className="relative">
<Highlight
theme={theme === "dark" ? themes.vsDark : themes.vsLight}
code={code.trim()}
language={language as any}
>
{({ className: highlightClassName, style, tokens, getLineProps, getTokenProps }) => (
<pre
className={cn(
"overflow-auto p-4 text-sm",
highlightClassName
)}
style={{
...style,
maxHeight,
backgroundColor: 'transparent'
}}
>
<code className="relative block">
{showLineNumbers ? (
<div className="flex">
{/* Line Numbers */}
<div className="mr-4 select-none border-r pr-4 text-muted-foreground">
{tokens.map((_, index) => (
<div key={index} className="text-right leading-6">
{index + 1}
</div>
))}
</div>
{/* Code Lines */}
<div className="flex-1">
{tokens.map((line, i) => (
<div key={i} {...getLineProps({ line })} className="leading-6">
{line.map((token, key) => (
<span key={key} {...getTokenProps({ token })} />
))}
</div>
))}
</div>
</div>
) : (
tokens.map((line, i) => (
<div key={i} {...getLineProps({ line })} className="leading-6">
{line.map((token, key) => (
<span key={key} {...getTokenProps({ token })} />
))}
</div>
))
)}
</code>
</pre>
)}
</Highlight>
</div>
</div>
)
}
)
CodeBlock.displayName = "CodeBlock"
export { CodeBlock, codeBlockVariants }

Usage

tsx
import { CodeBlock } from "@/components/ui/code-block"
tsx
<CodeBlock
language="tsx"
code={`console.log("Hello, World!")`}
/>

Examples

Basic

javascript
function greet(name) {
return `Hello, ${name}!`;
}
console.log(greet("World"));
tsx
<CodeBlock
language="javascript"
code={`function greet(name) {
return \`Hello, \${name}!\`;
}
console.log(greet("World"));`}
/>

With Filename

components/ui/button.tsx
import { Button } from "@/components/ui/button"
export function MyButton() {
return <Button variant="outline">Click me</Button>
}
tsx
<CodeBlock
language="tsx"
filename="components/ui/button.tsx"
code={`import { Button } from "@/components/ui/button"
export function MyButton() {
return <Button variant="outline">Click me</Button>
}`}
/>

With Line Numbers

app.py
1
2
3
4
5
6
7
8
def fibonacci(n):
if n <= 1:
return n
return fibonacci(n-1) + fibonacci(n-2)
# Generate first 10 fibonacci numbers
for i in range(10):
print(f"F({i}) = {fibonacci(i)}")
tsx
<CodeBlock
language="python"
filename="app.py"
showLineNumbers
code={`def fibonacci(n):
if n <= 1:
return n
return fibonacci(n-1) + fibonacci(n-2)
# Generate first 10 fibonacci numbers
for i in range(10):
print(f"F({i}) = {fibonacci(i)}")`}
/>

Ghost Variant

bash
npm install @radix-ui/react-slot
npm install class-variance-authority
npm install clsx tailwind-merge
tsx
<CodeBlock
variant="ghost"
language="bash"
code={`npm install @radix-ui/react-slot
npm install class-variance-authority
npm install clsx tailwind-merge`}
/>

Without Copy Button

package.json
{
"name": "my-app",
"version": "1.0.0",
"dependencies": {
"react": "^18.0.0",
"next": "^14.0.0"
}
}
tsx
<CodeBlock
language="json"
filename="package.json"
showCopy={false}
code={`{
"name": "my-app",
"version": "1.0.0",
"dependencies": {
"react": "^18.0.0",
"next": "^14.0.0"
}
}`}
/>

Custom Max Height

styles.css
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
/* Global Styles */
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Inter', sans-serif;
line-height: 1.6;
color: #333;
}
.container {
max-width: 1200px;
margin: 0 auto;
padding: 0 20px;
}
.header {
background-color: #fff;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
position: sticky;
top: 0;
z-index: 100;
}
.nav {
display: flex;
justify-content: space-between;
align-items: center;
padding: 1rem 0;
}
.logo {
font-size: 1.5rem;
font-weight: bold;
color: #2563eb;
}
.nav-links {
display: flex;
list-style: none;
gap: 2rem;
}
.nav-link {
text-decoration: none;
color: #6b7280;
font-weight: 500;
transition: color 0.2s;
}
.nav-link:hover {
color: #2563eb;
}
tsx
<CodeBlock
language="css"
filename="styles.css"
maxHeight="200px"
showLineNumbers
code={`/* Long CSS content... */`}
/>

Language Support

The CodeBlock component supports syntax highlighting for many languages:

query.sql
SELECT users.name, COUNT(orders.id) as order_count
FROM users
LEFT JOIN orders ON users.id = orders.user_id
WHERE users.created_at > '2024-01-01'
GROUP BY users.id, users.name
ORDER BY order_count DESC;
docker-compose.yml
version: '3.8'
services:
app:
build: .
ports:
- "3000:3000"
environment:
- NODE_ENV=production
volumes:
- ./src:/app/src
depends_on:
- db
db:
image: postgres:15
environment:
POSTGRES_DB: myapp
POSTGRES_PASSWORD: secret

API Reference

PropTypeDefaultDescription
codestring-The code content to display
languagestring"text"Programming language for syntax highlighting
filenamestring-Optional filename to display in header
showLineNumbersbooleanfalseWhether to show line numbers
showCopybooleantrueWhether to show copy button
maxHeightstring"400px"Maximum height of the code block
variant"default" | "ghost""default"Visual variant of the code block
theme"light" | "dark""dark"Syntax highlighting theme

CodeBlock also accepts all standard HTML div attributes. Built with prism-react-renderer for syntax highlighting.

View Prism Docs