CodeBlock
A syntax-highlighted code block component with copy functionality and language detection
Installation
Terminal
pnpm add @choice-ui/code-blockImport
Component.tsx
import { CodeBlock, CodeBlockHeader, CodeBlockFooter, CodeBlockContent, CodeBlockCode, type CodeBlockProps, type CodeBlockHeaderProps, type CodeBlockFooterProps, type CodeBlockContentProps, type CodeBlockCodeProps, getDefaultFilenameForLanguage } from "@choice-ui/code-block"Basic
Demonstrates different programming languages with automatic theme switching.
The theme automatically adapts to the system/app theme (light/dark).
export function add(a: number, b: number): number {
return a + b
}{"name":"prompt-kit","version":"1.0.0","private":true}diff --git a/index.ts b/index.ts
--- a/index.ts
+++ b/index.ts
@@
- console.log('hello')
+ console.info('hello world')
Long Code With Scroll
Shows how long code content is handled with vertical scrolling.
Useful for displaying large code files without taking up too much space.
console.log('line 1')
console.log('line 2')
console.log('line 3')
console.log('line 4')
console.log('line 5')
console.log('line 6')
console.log('line 7')
console.log('line 8')
console.log('line 9')
console.log('line 10')
console.log('line 11')
console.log('line 12')
console.log('line 13')
console.log('line 14')
console.log('line 15')
console.log('line 16')
console.log('line 17')
console.log('line 18')
console.log('line 19')
console.log('line 20')
console.log('line 21')
console.log('line 22')
console.log('line 23')
console.log('line 24')
console.log('line 25')
console.log('line 26')
console.log('line 27')
console.log('line 28')
console.log('line 29')
console.log('line 30')
console.log('line 31')
console.log('line 32')
console.log('line 33')
console.log('line 34')
console.log('line 35')
console.log('line 36')
console.log('line 37')
console.log('line 38')
console.log('line 39')
console.log('line 40')
console.log('line 41')
console.log('line 42')
console.log('line 43')
console.log('line 44')
console.log('line 45')
console.log('line 46')
console.log('line 47')
console.log('line 48')
console.log('line 49')
console.log('line 50')
console.log('line 51')
console.log('line 52')
console.log('line 53')
console.log('line 54')
console.log('line 55')
console.log('line 56')
console.log('line 57')
console.log('line 58')
console.log('line 59')
console.log('line 60')
console.log('line 61')
console.log('line 62')
console.log('line 63')
console.log('line 64')
console.log('line 65')
console.log('line 66')
console.log('line 67')
console.log('line 68')
console.log('line 69')
console.log('line 70')
console.log('line 71')
console.log('line 72')
console.log('line 73')
console.log('line 74')
console.log('line 75')
console.log('line 76')
console.log('line 77')
console.log('line 78')
console.log('line 79')
console.log('line 80')Long Code Without Scroll
Demonstrates code block without scroll area container.
Use this when you want the code to naturally extend the container height.
console.log('line 1')
console.log('line 2')
console.log('line 3')
console.log('line 4')
console.log('line 5')
console.log('line 6')
console.log('line 7')
console.log('line 8')
console.log('line 9')
console.log('line 10')
console.log('line 11')
console.log('line 12')
console.log('line 13')
console.log('line 14')
console.log('line 15')
console.log('line 16')
console.log('line 17')
console.log('line 18')
console.log('line 19')
console.log('line 20')
console.log('line 21')
console.log('line 22')
console.log('line 23')
console.log('line 24')
console.log('line 25')
console.log('line 26')
console.log('line 27')
console.log('line 28')
console.log('line 29')
console.log('line 30')
console.log('line 31')
console.log('line 32')
console.log('line 33')
console.log('line 34')
console.log('line 35')
console.log('line 36')
console.log('line 37')
console.log('line 38')
console.log('line 39')
console.log('line 40')
console.log('line 41')
console.log('line 42')
console.log('line 43')
console.log('line 44')
console.log('line 45')
console.log('line 46')
console.log('line 47')
console.log('line 48')
console.log('line 49')
console.log('line 50')
console.log('line 51')
console.log('line 52')
console.log('line 53')
console.log('line 54')
console.log('line 55')
console.log('line 56')
console.log('line 57')
console.log('line 58')
console.log('line 59')
console.log('line 60')
console.log('line 61')
console.log('line 62')
console.log('line 63')
console.log('line 64')
console.log('line 65')
console.log('line 66')
console.log('line 67')
console.log('line 68')
console.log('line 69')
console.log('line 70')
console.log('line 71')
console.log('line 72')
console.log('line 73')
console.log('line 74')
console.log('line 75')
console.log('line 76')
console.log('line 77')
console.log('line 78')
console.log('line 79')
console.log('line 80')Variants
Code block with different variants.
file.txt
console.log('Hello, world!')file.txt
console.log('Hello, world!')file.txt
console.log('Hello, world!')Popular Languages
Showcases popular programming and markup languages.
Each language has proper syntax highlighting powered by Shiki.
import React from 'react'
export function Button({ label }: { label: string }) {
return (
<button className="px-3 py-1 rounded border">{label}</button>
)
}<!doctype html>
<html>
<head>
<meta charset="utf-8" />
<title>Example</title>
</head>
<body>
<div id="app">Hello</div>
</body>
</html>(() => {
const msg = 'hello';
console.log(msg);
})();:root {
--primary: #0ea5e9;
}
.button {
background: var(--primary);
color: white;
}def add(a: int, b: int) -> int:
return a + b
print(add(2, 3))Long Tsx Demo
Real-world example with a complex React component.
Demonstrates how readable long code remains with syntax highlighting.
import React, { useState, useEffect, useCallback, useMemo } from "react"
import {
ChevronDown,
ChevronRight,
Code2,
Copy,
Check,
Terminal,
FileCode,
Sparkles
} from "lucide-react"
import { motion, AnimatePresence } from "framer-motion"
import { tcx } from "~/utils"
interface CodeEditorProps {
initialCode?: string
language?: "typescript" | "javascript" | "python" | "rust" | "go"
theme?: "dark" | "light"
onCodeChange?: (code: string) => void
readOnly?: boolean
showLineNumbers?: boolean
highlightLines?: number[]
className?: string
}
export const CodeEditor: React.FC<CodeEditorProps> = ({
initialCode = "",
language = "typescript",
theme = "dark",
onCodeChange,
readOnly = false,
showLineNumbers = true,
highlightLines = [],
className
}) => {
const [code, setCode] = useState(initialCode)
const [copied, setCopied] = useState(false)
const [isExpanded, setIsExpanded] = useState(true)
const [hoveredLine, setHoveredLine] = useState<number | null>(null)
// Memoize syntax highlighting
const highlightedCode = useMemo(() => {
return highlightSyntax(code, language)
}, [code, language])
// Handle code changes
const handleCodeChange = useCallback((newCode: string) => {
if (!readOnly) {
setCode(newCode)
onCodeChange?.(newCode)
}
}, [readOnly, onCodeChange])
// Copy to clipboard functionality
const copyToClipboard = useCallback(async () => {
try {
await navigator.clipboard.writeText(code)
setCopied(true)
setTimeout(() => setCopied(false), 2000)
} catch (err) {
console.error("Failed to copy:", err)
}
}, [code])
// Auto-save functionality
useEffect(() => {
const autoSaveTimer = setTimeout(() => {
if (code !== initialCode) {
console.log("Auto-saving code...")
// Implement auto-save logic here
}
}, 2000)
return () => clearTimeout(autoSaveTimer)
}, [code, initialCode])
// Keyboard shortcuts
useEffect(() => {
const handleKeyPress = (e: KeyboardEvent) => {
if ((e.metaKey || e.ctrlKey) && e.key === "s") {
e.preventDefault()
console.log("Saving code...")
}
if ((e.metaKey || e.ctrlKey) && e.shiftKey && e.key === "f") {
e.preventDefault()
console.log("Formatting code...")
}
}
window.addEventListener("keydown", handleKeyPress)
return () => window.removeEventListener("keydown", handleKeyPress)
}, [])
return (
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.3 }}
className={tcx(
"relative rounded-lg overflow-hidden shadow-2xl",
theme === "dark" ? "bg-gray-900" : "bg-white",
className
)}
>
{/* Header */}
<div className={tcx(
"flex items-center justify-between px-4 py-3 border-b",
theme === "dark" ? "bg-gray-800 border-gray-700" : "bg-gray-50 border-gray-200"
)}>
<div className="flex items-center gap-3">
<button
onClick={() => setIsExpanded(!isExpanded)}
className="p-1 rounded hover:bg-gray-700 transition-colors"
>
{isExpanded ? <ChevronDown size={16} /> : <ChevronRight size={16} />}
</button>
<FileCode size={18} className="text-blue-500" />
<span className="text-body-small font-mono">{language}</span>
</div>
<div className="flex items-center gap-2">
<button
onClick={copyToClipboard}
className={tcx(
"p-2 rounded transition-all",
theme === "dark" ? "hover:bg-gray-700" : "hover:bg-gray-200"
)}
>
<AnimatePresence mode="wait">
{copied ? (
<motion.div
initial={{ scale: 0 }}
animate={{ scale: 1 }}
exit={{ scale: 0 }}
>
<Check size={16} className="text-green-500" />
</motion.div>
) : (
<Copy size={16} />
)}
</AnimatePresence>
</button>
</div>
</div>
{/* Code Content */}
<AnimatePresence>
{isExpanded && (
<motion.div
initial={{ height: 0 }}
animate={{ height: "auto" }}
exit={{ height: 0 }}
transition={{ duration: 0.2 }}
className="overflow-hidden"
>
<div className="relative">
{showLineNumbers && (
<div className={tcx(
"absolute left-0 top-0 bottom-0 w-12 text-right pr-2",
theme === "dark" ? "bg-gray-850 text-gray-500" : "bg-gray-50 text-gray-400"
)}>
{code.split("\n").map((_, index) => (
<div
key={index}
className={tcx(
"text-xs leading-6 font-mono",
highlightLines.includes(index + 1) && "text-yellow-500 font-bold"
)}
>
{index + 1}
</div>
))}
</div>
)}
<div
className={tcx(
"p-4 overflow-x-auto",
showLineNumbers && "pl-16",
theme === "dark" ? "bg-gray-900" : "bg-white"
)}
contentEditable={!readOnly}
suppressContentEditableWarning
onInput={(e) => handleCodeChange(e.currentTarget.textContent || "")}
style={{ minHeight: "200px" }}
>
<pre className="font-mono text-body-small leading-6">
<code dangerouslySetInnerHTML={{ __html: highlightedCode }} />
</pre>
</div>
</div>
</motion.div>
)}
</AnimatePresence>
{/* Footer Status Bar */}
<div className={tcx(
"flex items-center justify-between px-4 py-2 text-xs",
theme === "dark" ? "bg-gray-800 text-gray-400" : "bg-gray-50 text-gray-600"
)}>
<div className="flex items-center gap-4">
<span>Lines: {code.split("\n").length}</span>
<span>Characters: {code.length}</span>
</div>
<div className="flex items-center gap-2">
<Sparkles size={12} />
<span>AI-powered suggestions available</span>
</div>
</div>
</motion.div>
)
}
// Utility function for syntax highlighting (simplified)
function highlightSyntax(code: string, language: string): string {
// This is a simplified version - in production, use a proper syntax highlighter
const keywords = {
typescript: ["const", "let", "var", "function", "return", "if", "else", "for", "while", "class", "interface", "type", "import", "export", "from"],
javascript: ["const", "let", "var", "function", "return", "if", "else", "for", "while", "class"],
python: ["def", "class", "import", "from", "return", "if", "else", "for", "while", "in"],
rust: ["fn", "let", "mut", "const", "struct", "impl", "trait", "use", "mod", "pub"],
go: ["func", "var", "const", "type", "struct", "interface", "import", "package", "return"]
}
let highlighted = code
keywords[language]?.forEach(keyword => {
const regex = new RegExp(`\\b${keyword}\\b`, "g")
highlighted = highlighted.replace(regex, `<span class="text-blue-500">${keyword}</span>`)
})
return highlighted
}
export default CodeEditorWith Interactive Header
Code block with interactive header showing filename and controls.
Header includes language icon, filename, copy button, and expand/collapse toggle.
HelloWorld.tsx
import React from 'react'
export function Button({ label }: { label: string }) {
return (
<button className="px-3 py-1 rounded border">{label}</button>
)
}With Header And Footer
Full featured code block with header and footer.
Footer appears when code exceeds the line threshold, showing expand controls.
LongComponent.tsx
import React, { useState, useEffect, useCallback, useMemo } from "react"
import {
ChevronDown,
ChevronRight,
Code2,
Copy,
Check,
Terminal,
FileCode,
Sparkles
} from "lucide-react"
import { motion, AnimatePresence } from "framer-motion"
import { tcx } from "~/utils"
interface CodeEditorProps {
initialCode?: string
language?: "typescript" | "javascript" | "python" | "rust" | "go"
theme?: "dark" | "light"
onCodeChange?: (code: string) => void
readOnly?: boolean
showLineNumbers?: boolean
highlightLines?: number[]
className?: string
}
export const CodeEditor: React.FC<CodeEditorProps> = ({
initialCode = "",
language = "typescript",
theme = "dark",
onCodeChange,
readOnly = false,
showLineNumbers = true,
highlightLines = [],
className
}) => {
const [code, setCode] = useState(initialCode)
const [copied, setCopied] = useState(false)
const [isExpanded, setIsExpanded] = useState(true)
const [hoveredLine, setHoveredLine] = useState<number | null>(null)
// Memoize syntax highlighting
const highlightedCode = useMemo(() => {
return highlightSyntax(code, language)
}, [code, language])
// Handle code changes
const handleCodeChange = useCallback((newCode: string) => {
if (!readOnly) {
setCode(newCode)
onCodeChange?.(newCode)
}
}, [readOnly, onCodeChange])
// Copy to clipboard functionality
const copyToClipboard = useCallback(async () => {
try {
await navigator.clipboard.writeText(code)
setCopied(true)
setTimeout(() => setCopied(false), 2000)
} catch (err) {
console.error("Failed to copy:", err)
}
}, [code])
// Auto-save functionality
useEffect(() => {
const autoSaveTimer = setTimeout(() => {
if (code !== initialCode) {
console.log("Auto-saving code...")
// Implement auto-save logic here
}
}, 2000)
return () => clearTimeout(autoSaveTimer)
}, [code, initialCode])
// Keyboard shortcuts
useEffect(() => {
const handleKeyPress = (e: KeyboardEvent) => {
if ((e.metaKey || e.ctrlKey) && e.key === "s") {
e.preventDefault()
console.log("Saving code...")
}
if ((e.metaKey || e.ctrlKey) && e.shiftKey && e.key === "f") {
e.preventDefault()
console.log("Formatting code...")
}
}
window.addEventListener("keydown", handleKeyPress)
return () => window.removeEventListener("keydown", handleKeyPress)
}, [])
return (
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.3 }}
className={tcx(
"relative rounded-lg overflow-hidden shadow-2xl",
theme === "dark" ? "bg-gray-900" : "bg-white",
className
)}
>
{/* Header */}
<div className={tcx(
"flex items-center justify-between px-4 py-3 border-b",
theme === "dark" ? "bg-gray-800 border-gray-700" : "bg-gray-50 border-gray-200"
)}>
<div className="flex items-center gap-3">
<button
onClick={() => setIsExpanded(!isExpanded)}
className="p-1 rounded hover:bg-gray-700 transition-colors"
>
{isExpanded ? <ChevronDown size={16} /> : <ChevronRight size={16} />}
</button>
<FileCode size={18} className="text-blue-500" />
<span className="text-body-small font-mono">{language}</span>
</div>
<div className="flex items-center gap-2">
<button
onClick={copyToClipboard}
className={tcx(
"p-2 rounded transition-all",
theme === "dark" ? "hover:bg-gray-700" : "hover:bg-gray-200"
)}
>
<AnimatePresence mode="wait">
{copied ? (
<motion.div
initial={{ scale: 0 }}
animate={{ scale: 1 }}
exit={{ scale: 0 }}
>
<Check size={16} className="text-green-500" />
</motion.div>
) : (
<Copy size={16} />
)}
</AnimatePresence>
</button>
</div>
</div>
{/* Code Content */}
<AnimatePresence>
{isExpanded && (
<motion.div
initial={{ height: 0 }}
animate={{ height: "auto" }}
exit={{ height: 0 }}
transition={{ duration: 0.2 }}
className="overflow-hidden"
>
<div className="relative">
{showLineNumbers && (
<div className={tcx(
"absolute left-0 top-0 bottom-0 w-12 text-right pr-2",
theme === "dark" ? "bg-gray-850 text-gray-500" : "bg-gray-50 text-gray-400"
)}>
{code.split("\n").map((_, index) => (
<div
key={index}
className={tcx(
"text-xs leading-6 font-mono",
highlightLines.includes(index + 1) && "text-yellow-500 font-bold"
)}
>
{index + 1}
</div>
))}
</div>
)}
<div
className={tcx(
"p-4 overflow-x-auto",
showLineNumbers && "pl-16",
theme === "dark" ? "bg-gray-900" : "bg-white"
)}
contentEditable={!readOnly}
suppressContentEditableWarning
onInput={(e) => handleCodeChange(e.currentTarget.textContent || "")}
style={{ minHeight: "200px" }}
>
<pre className="font-mono text-body-small leading-6">
<code dangerouslySetInnerHTML={{ __html: highlightedCode }} />
</pre>
</div>
</div>
</motion.div>
)}
</AnimatePresence>
{/* Footer Status Bar */}
<div className={tcx(
"flex items-center justify-between px-4 py-2 text-xs",
theme === "dark" ? "bg-gray-800 text-gray-400" : "bg-gray-50 text-gray-600"
)}>
<div className="flex items-center gap-4">
<span>Lines: {code.split("\n").length}</span>
<span>Characters: {code.length}</span>
</div>
<div className="flex items-center gap-2">
<Sparkles size={12} />
<span>AI-powered suggestions available</span>
</div>
</div>
</motion.div>
)
}
// Utility function for syntax highlighting (simplified)
function highlightSyntax(code: string, language: string): string {
// This is a simplified version - in production, use a proper syntax highlighter
const keywords = {
typescript: ["const", "let", "var", "function", "return", "if", "else", "for", "while", "class", "interface", "type", "import", "export", "from"],
javascript: ["const", "let", "var", "function", "return", "if", "else", "for", "while", "class"],
python: ["def", "class", "import", "from", "return", "if", "else", "for", "while", "in"],
rust: ["fn", "let", "mut", "const", "struct", "impl", "trait", "use", "mod", "pub"],
go: ["func", "var", "const", "type", "struct", "interface", "import", "package", "return"]
}
let highlighted = code
keywords[language]?.forEach(keyword => {
const regex = new RegExp(`\\b${keyword}\\b`, "g")
highlighted = highlighted.replace(regex, `<span class="text-blue-500">${keyword}</span>`)
})
return highlighted
}
export default CodeEditorCollapsed By Default
Code block collapsed by default to save space.
Users can click the expand button to reveal the code content.
example.py
Expanded Code
Long code that starts fully expanded, showing all lines.
Useful when you want to display complete code without initial truncation.
LongFile.tsx
import React, { useState, useEffect, useCallback, useMemo } from "react"
import {
ChevronDown,
ChevronRight,
Code2,
Copy,
Check,
Terminal,
FileCode,
Sparkles
} from "lucide-react"
import { motion, AnimatePresence } from "framer-motion"
import { tcx } from "~/utils"
interface CodeEditorProps {
initialCode?: string
language?: "typescript" | "javascript" | "python" | "rust" | "go"
theme?: "dark" | "light"
onCodeChange?: (code: string) => void
readOnly?: boolean
showLineNumbers?: boolean
highlightLines?: number[]
className?: string
}
export const CodeEditor: React.FC<CodeEditorProps> = ({
initialCode = "",
language = "typescript",
theme = "dark",
onCodeChange,
readOnly = false,
showLineNumbers = true,
highlightLines = [],
className
}) => {
const [code, setCode] = useState(initialCode)
const [copied, setCopied] = useState(false)
const [isExpanded, setIsExpanded] = useState(true)
const [hoveredLine, setHoveredLine] = useState<number | null>(null)
// Memoize syntax highlighting
const highlightedCode = useMemo(() => {
return highlightSyntax(code, language)
}, [code, language])
// Handle code changes
const handleCodeChange = useCallback((newCode: string) => {
if (!readOnly) {
setCode(newCode)
onCodeChange?.(newCode)
}
}, [readOnly, onCodeChange])
// Copy to clipboard functionality
const copyToClipboard = useCallback(async () => {
try {
await navigator.clipboard.writeText(code)
setCopied(true)
setTimeout(() => setCopied(false), 2000)
} catch (err) {
console.error("Failed to copy:", err)
}
}, [code])
// Auto-save functionality
useEffect(() => {
const autoSaveTimer = setTimeout(() => {
if (code !== initialCode) {
console.log("Auto-saving code...")
// Implement auto-save logic here
}
}, 2000)
return () => clearTimeout(autoSaveTimer)
}, [code, initialCode])
// Keyboard shortcuts
useEffect(() => {
const handleKeyPress = (e: KeyboardEvent) => {
if ((e.metaKey || e.ctrlKey) && e.key === "s") {
e.preventDefault()
console.log("Saving code...")
}
if ((e.metaKey || e.ctrlKey) && e.shiftKey && e.key === "f") {
e.preventDefault()
console.log("Formatting code...")
}
}
window.addEventListener("keydown", handleKeyPress)
return () => window.removeEventListener("keydown", handleKeyPress)
}, [])
return (
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.3 }}
className={tcx(
"relative rounded-lg overflow-hidden shadow-2xl",
theme === "dark" ? "bg-gray-900" : "bg-white",
className
)}
>
{/* Header */}
<div className={tcx(
"flex items-center justify-between px-4 py-3 border-b",
theme === "dark" ? "bg-gray-800 border-gray-700" : "bg-gray-50 border-gray-200"
)}>
<div className="flex items-center gap-3">
<button
onClick={() => setIsExpanded(!isExpanded)}
className="p-1 rounded hover:bg-gray-700 transition-colors"
>
{isExpanded ? <ChevronDown size={16} /> : <ChevronRight size={16} />}
</button>
<FileCode size={18} className="text-blue-500" />
<span className="text-body-small font-mono">{language}</span>
</div>
<div className="flex items-center gap-2">
<button
onClick={copyToClipboard}
className={tcx(
"p-2 rounded transition-all",
theme === "dark" ? "hover:bg-gray-700" : "hover:bg-gray-200"
)}
>
<AnimatePresence mode="wait">
{copied ? (
<motion.div
initial={{ scale: 0 }}
animate={{ scale: 1 }}
exit={{ scale: 0 }}
>
<Check size={16} className="text-green-500" />
</motion.div>
) : (
<Copy size={16} />
)}
</AnimatePresence>
</button>
</div>
</div>
{/* Code Content */}
<AnimatePresence>
{isExpanded && (
<motion.div
initial={{ height: 0 }}
animate={{ height: "auto" }}
exit={{ height: 0 }}
transition={{ duration: 0.2 }}
className="overflow-hidden"
>
<div className="relative">
{showLineNumbers && (
<div className={tcx(
"absolute left-0 top-0 bottom-0 w-12 text-right pr-2",
theme === "dark" ? "bg-gray-850 text-gray-500" : "bg-gray-50 text-gray-400"
)}>
{code.split("\n").map((_, index) => (
<div
key={index}
className={tcx(
"text-xs leading-6 font-mono",
highlightLines.includes(index + 1) && "text-yellow-500 font-bold"
)}
>
{index + 1}
</div>
))}
</div>
)}
<div
className={tcx(
"p-4 overflow-x-auto",
showLineNumbers && "pl-16",
theme === "dark" ? "bg-gray-900" : "bg-white"
)}
contentEditable={!readOnly}
suppressContentEditableWarning
onInput={(e) => handleCodeChange(e.currentTarget.textContent || "")}
style={{ minHeight: "200px" }}
>
<pre className="font-mono text-body-small leading-6">
<code dangerouslySetInnerHTML={{ __html: highlightedCode }} />
</pre>
</div>
</div>
</motion.div>
)}
</AnimatePresence>
{/* Footer Status Bar */}
<div className={tcx(
"flex items-center justify-between px-4 py-2 text-xs",
theme === "dark" ? "bg-gray-800 text-gray-400" : "bg-gray-50 text-gray-600"
)}>
<div className="flex items-center gap-4">
<span>Lines: {code.split("\n").length}</span>
<span>Characters: {code.length}</span>
</div>
<div className="flex items-center gap-2">
<Sparkles size={12} />
<span>AI-powered suggestions available</span>
</div>
</div>
</motion.div>
)
}
// Utility function for syntax highlighting (simplified)
function highlightSyntax(code: string, language: string): string {
// This is a simplified version - in production, use a proper syntax highlighter
const keywords = {
typescript: ["const", "let", "var", "function", "return", "if", "else", "for", "while", "class", "interface", "type", "import", "export", "from"],
javascript: ["const", "let", "var", "function", "return", "if", "else", "for", "while", "class"],
python: ["def", "class", "import", "from", "return", "if", "else", "for", "while", "in"],
rust: ["fn", "let", "mut", "const", "struct", "impl", "trait", "use", "mod", "pub"],
go: ["func", "var", "const", "type", "struct", "interface", "import", "package", "return"]
}
let highlighted = code
keywords[language]?.forEach(keyword => {
const regex = new RegExp(`\\b${keyword}\\b`, "g")
highlighted = highlighted.replace(regex, `<span class="text-blue-500">${keyword}</span>`)
})
return highlighted
}
export default CodeEditorNon Expandable
Code block with expand/collapse functionality disabled.
Use this for short code snippets that don't need truncation.
config.json
{"name":"prompt-kit","version":"1.0.0","private":true}Internationalization
Demonstrates internationalization support with custom labels.
You can provide custom translations for buttons and tooltips.
国际化示例.tsx
import React from 'react'
export function Button({ label }: { label: string }) {
return (
<button className="px-3 py-1 rounded border">{label}</button>
)
}Language Icons
Comprehensive showcase of language-specific icons in the header.
Icons are automatically selected based on the language or filename.
Demonstrates JavaScript, TypeScript, markup, config files, and more.
script.js
console.log('Hello, World!');Component.jsx
export const Button = () => <button>Click</button>example.ts
export function add(a: number, b: number): number {
return a + b
}App.tsx
import React from 'react'
export function Button({ label }: { label: string }) {
return (
<button className="px-3 py-1 rounded border">{label}</button>
)
}types.d.ts
declare module 'my-lib' {
export function hello(): string
}styles.css
:root {
--primary: #0ea5e9;
}
.button {
background: var(--primary);
color: white;
}theme.scss
$primary: #0ea5e9;
.button {
color: $primary;
}index.html
<!doctype html>
<html>
<head>
<meta charset="utf-8" />
<title>Example</title>
</head>
<body>
<div id="app">Hello</div>
</body>
</html>config.xml
<?xml version="1.0"?>
<root>
<item>value</item>
</root>README.md
# Hello World
This is a markdown file.docs.mdx
# Component Docs
import { Button } from './Button'
<Button />package.json
{"name":"prompt-kit","version":"1.0.0","private":true}config.yml
name: My App
version: 1.0.0
scripts:
- build
- testdeploy.sh
#!/bin/bash
echo "Deploying..."
npm run buildsetup.sh
#!/bin/sh
export NODE_ENV=productionmain.py
def add(a: int, b: int) -> int:
return a + b
print(add(2, 3))server.js
const express = require('express')
const app = express()
app.listen(3000)notes.txt
This is a plain text file.
No syntax highlighting.main.go
package main
func main() {
println("Hello")
}main.rs
fn main() {
println!("Hello");
}Main.java
public class Main {
public static void main(String[] args) {
System.out.println("Hello");
}
}Default Filename
component.tsx
import React from 'react'
export function Button({ label }: { label: string }) {
return (
<button className="px-3 py-1 rounded border">{label}</button>
)
}Line Count Threshold
Shows how the lineThreshold property controls when footer appears.
Footer with expand controls only shows when code exceeds the threshold.
Useful for managing space with varying code lengths.
10 lines (no footer, below threshold)
short.js
// Short code (5 lines)
function greet(name) {
return `Hello, ${name}!`;
}
console.log(greet('World'));12 lines (footer shown, above threshold)
medium.js
// Medium code (12 lines)
import React from 'react';
function TodoItem({ todo, onToggle }) {
return (
<li className="todo-item">
<input
type="checkbox"
checked={todo.completed}
onChange={() => onToggle(todo.id)}
/>
<span>{todo.text}</span>
</li>
);
}Custom threshold (3 lines)
custom.js
// Short code (5 lines)
function greet(name) {
return `Hello, ${name}!`;
}
console.log(greet('World'));Streaming With Auto Scroll
Streaming code blocks simulation with auto-scroll to bottom.
Uses useStickToBottom hook to keep scroll position at bottom as new code blocks appear.
Click "Add Code Block" to simulate streaming new content.
Blocks: 0 | At bottom: Yes
Click "Add Code Block" or "Auto Stream" to start
Character Streaming
Simulates character-by-character streaming of code content.
Content appears gradually like AI-generated code output.
useUser.ts
// Code will appear here...API reference
| CodeBlockRoot | Type | Default |
|---|---|---|
className | string |undefined | - |
defaultCodeExpanded | boolean |undefined | - |
defaultExpanded | boolean |undefined | - |
expandable | boolean |undefined | - |
filename | string |undefined | - |
language | string |undefined | - |
lineThreshold | number |undefined | - |
onCodeExpandChange | ((expanded: boolean) => void) |undefined | - |
onExpandChange | ((expanded: boolean) => void) |undefined | - |
variant | undefined |"default" |"light" |"dark" | - |
| CodeBlockProps | Type | Default |
className | string |undefined | - |
defaultCodeExpanded | boolean |undefined | - |
defaultExpanded | boolean |undefined | - |
expandable | boolean |undefined | - |
filename | string |undefined | - |
language | string |undefined | - |
lineThreshold | number |undefined | - |
onCodeExpandChange | ((expanded: boolean) => void) |undefined | - |
onExpandChange | ((expanded: boolean) => void) |undefined | - |
variant | undefined |"default" |"light" |"dark" | - |