UI Components Easy
Code Block (React)
Syntax-highlighted code block with copy button, line numbers, and highlighted lines. React + Tailwind.
react tailwind
Targets: TS React
Code
import { useState, useCallback } from "react";
const TOKENS = [
{ cls: "text-red-400", re: /\/\/[^\n]*/g },
{ cls: "text-sky-300", re: /(['"`])(?:(?!\1)[^\\]|\\.)*\1/g },
{
cls: "text-rose-400",
re: /\b(async|await|function|return|const|let|var|if|else|throw|new|import|export|interface|type|class|for|of|in|extends)\b/g,
},
{ cls: "text-amber-300", re: /\b(Promise|Error|string|number|boolean|void|null|undefined)\b/g },
{ cls: "text-purple-300", re: /\b([a-zA-Z_$][a-zA-Z0-9_$]*)(?=\s*\()/g },
{ cls: "text-blue-300", re: /\b\d+(\.\d+)?\b/g },
];
function tokenize(code: string) {
const safe = code.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
const matches: { start: number; end: number; cls: string; text: string }[] = [];
for (const { cls, re } of TOKENS) {
re.lastIndex = 0;
let m: RegExpExecArray | null;
while ((m = re.exec(safe)) !== null) {
matches.push({ start: m.index, end: m.index + m[0].length, cls, text: m[0] });
}
}
matches.sort((a, b) => a.start - b.start);
const used = matches.filter((t, i) => i === 0 || t.start >= matches[i - 1].end);
let html = "";
let cursor = 0;
for (const { start, end, cls, text } of used) {
html += safe.slice(cursor, start);
html += `<span class="${cls}">${text}</span>`;
cursor = end;
}
html += safe.slice(cursor);
return html;
}
interface CodeBlockProps {
code: string;
language?: string;
showLines?: boolean;
highlight?: number[];
}
function CodeBlock({
code,
language = "JavaScript",
showLines = false,
highlight = [],
}: CodeBlockProps) {
const [copied, setCopied] = useState(false);
const hlSet = new Set(highlight);
const showLineNums = showLines || highlight.length > 0;
const highlighted = tokenize(code);
const lines = highlighted.split("\n");
const handleCopy = useCallback(async () => {
await navigator.clipboard.writeText(code);
setCopied(true);
setTimeout(() => setCopied(false), 2000);
}, [code]);
return (
<div className="rounded-xl overflow-hidden border border-[#30363d]">
<div className="flex items-center justify-between px-4 py-2.5 bg-[#21262d] border-b border-[#30363d]">
<span className="text-xs font-semibold text-[#8b949e] uppercase tracking-wide">
{language}
</span>
<button
onClick={handleCopy}
className={`flex items-center gap-1.5 text-xs font-semibold px-2.5 py-1 rounded-md border transition-colors ${
copied
? "text-green-400 border-green-400"
: "text-[#8b949e] border-[#30363d] hover:text-white hover:border-[#8b949e]"
}`}
>
{copied ? (
<>
<svg
width="12"
height="12"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth="2.5"
>
<polyline points="20 6 9 17 4 12" />
</svg>
Copied!
</>
) : (
<>
<svg
width="12"
height="12"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth="2"
>
<rect x="9" y="9" width="13" height="13" rx="2" />
<path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1" />
</svg>
Copy
</>
)}
</button>
</div>
<pre className="m-0 overflow-x-auto bg-[#161b22] text-[13px] leading-[1.7] font-mono">
{showLineNums ? (
<code className="block">
{lines.map((line, i) => {
const lineNum = i + 1;
const isHL = hlSet.has(lineNum);
return (
<span
key={i}
className={`flex ${isHL ? "bg-yellow-400/[0.08] border-l-2 border-yellow-400 -ml-[1px]" : ""}`}
>
<span className="select-none text-right text-[11px] text-[#4a555f] pl-4 pr-3 min-w-[44px] flex-shrink-0">
{lineNum}
</span>
<span
className={`flex-1 pr-4 ${isHL ? "text-yellow-100" : ""}`}
dangerouslySetInnerHTML={{ __html: line }}
/>
</span>
);
})}
</code>
) : (
<code
className="block px-4 py-4 text-[#e6edf3]"
dangerouslySetInnerHTML={{ __html: highlighted }}
/>
)}
</pre>
</div>
);
}
const JS_CODE = `async function fetchUser(id) {
const res = await fetch(\`/api/users/\${id}\`);
if (!res.ok) throw new Error('Not found');
return res.json();
}
fetchUser(42).then(user => {
console.log(user.name);
}).catch(console.error);`;
const TS_CODE = `interface User {
id: number;
name: string;
email: string;
}
function getUser(id: number): Promise<User> {
return fetch(\`/api/users/\${id}\`).then(r => r.json());
}`;
export default function CodeBlockDemo() {
return (
<div className="min-h-screen bg-[#0d1117] p-8 flex justify-center">
<div className="w-full max-width-[680px] space-y-6">
<div>
<p className="text-[11px] font-bold text-[#8b949e] uppercase tracking-wider mb-2.5">
JavaScript
</p>
<CodeBlock code={JS_CODE} language="JavaScript" />
</div>
<div>
<p className="text-[11px] font-bold text-[#8b949e] uppercase tracking-wider mb-2.5">
TypeScript — highlighted lines
</p>
<CodeBlock code={TS_CODE} language="TypeScript" showLines highlight={[7, 8]} />
</div>
</div>
</div>
);
}React component version of the code block with syntax highlighting, copy-to-clipboard with success state, optional line numbers, and highlighted line support.