Code Block
A configurable code block component built on top of react-syntax-highlighter.
Open in
DummyComponent.jsx
1const DummyComponent = () => {
2 const [count, setCount] = React.useState(0);
3
4 const handleClick = () => {
5 setCount(prev => prev + 1);
6 };
7
8 return (
9 <div className="p-4 border rounded-lg">
10 <h2 className="text-xl font-bold mb-4">Fights Counter</h2>
11 <p className="mb-2">Fight Club Fights Count: {count}</p>
12 <button
13 onClick={handleClick}
14 className="px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-600"
15 >
16 Increment
17 </button>
18 </div>
19 );
20};
21
Installation
Install dependencies
npm i motion clsx tailwind-merge
Install code editor dependencies
npm i react-syntax-highlighter @types/react-syntax-highlighter @tabler/icons-react
For React 19 / Next.js 15 users, follow the following packages
For React 19 / Next.js 15 users, either use the --legacy-peer-deps
flag or add the following overrides in your package.json
file:
"overrides": {
"react-syntax-highlighter": "15.0.0"
}
Add util file
lib/utils.ts
import { ClassValue, clsx } from "clsx";
import { twMerge } from "tailwind-merge";
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs));
}
Copy the source code
components/ui/code-block.tsx
"use client";
import React from "react";
import { Prism as SyntaxHighlighter } from "react-syntax-highlighter";
import { atomDark } from "react-syntax-highlighter/dist/cjs/styles/prism";
import { IconCheck, IconCopy } from "@tabler/icons-react";
type CodeBlockProps = {
language: string;
filename: string;
highlightLines?: number[];
} & (
| {
code: string;
tabs?: never;
}
| {
code?: never;
tabs: Array<{
name: string;
code: string;
language?: string;
highlightLines?: number[];
}>;
}
);
export const CodeBlock = ({
language,
filename,
code,
highlightLines = [],
tabs = [],
}: CodeBlockProps) => {
const [copied, setCopied] = React.useState(false);
const [activeTab, setActiveTab] = React.useState(0);
const tabsExist = tabs.length > 0;
const copyToClipboard = async () => {
const textToCopy = tabsExist ? tabs[activeTab].code : code;
if (textToCopy) {
await navigator.clipboard.writeText(textToCopy);
setCopied(true);
setTimeout(() => setCopied(false), 2000);
}
};
const activeCode = tabsExist ? tabs[activeTab].code : code;
const activeLanguage = tabsExist
? tabs[activeTab].language || language
: language;
const activeHighlightLines = tabsExist
? tabs[activeTab].highlightLines || []
: highlightLines;
return (
<div className="relative w-full rounded-lg bg-slate-900 p-4 font-mono text-sm">
<div className="flex flex-col gap-2">
{tabsExist && (
<div className="flex overflow-x-auto">
{tabs.map((tab, index) => (
<button
key={index}
onClick={() => setActiveTab(index)}
className={`px-3 !py-2 text-xs transition-colors font-sans ${
activeTab === index
? "text-white"
: "text-zinc-400 hover:text-zinc-200"
}`}
>
{tab.name}
</button>
))}
</div>
)}
{!tabsExist && filename && (
<div className="flex justify-between items-center py-2">
<div className="text-xs text-zinc-400">{filename}</div>
<button
onClick={copyToClipboard}
className="flex items-center gap-1 text-xs text-zinc-400 hover:text-zinc-200 transition-colors font-sans"
>
{copied ? <IconCheck size={14} /> : <IconCopy size={14} />}
</button>
</div>
)}
</div>
<SyntaxHighlighter
language={activeLanguage}
style={atomDark}
customStyle={{
margin: 0,
padding: 0,
background: "transparent",
fontSize: "0.875rem", // text-sm equivalent
}}
wrapLines={true}
showLineNumbers={true}
lineProps={(lineNumber) => ({
style: {
backgroundColor: activeHighlightLines.includes(lineNumber)
? "rgba(255,255,255,0.1)"
: "transparent",
display: "block",
width: "100%",
},
})}
PreTag="div"
>
{String(activeCode)}
</SyntaxHighlighter>
</div>
);
};
Props
Prop | Type | Required | Description |
---|---|---|---|
language | string | Yes | The programming language for syntax highlighting |
filename | string | Yes | The name of the file to display |
highlightLines | number[] | No | Array of line numbers to highlight. Defaults to [] |
code | string | Conditional | The code content to display |
tabs | Array<TabConfig> | Conditional | Array of tab configurations |
Note: Either code OR tabs must be provided, but not both.
TabConfig Object Structure
Property | Type | Required | Description |
---|---|---|---|
name | string | Yes | The name of the tab |
code | string | Yes | The code content for this tab |
language | string | No | Override the default language for this tab |
highlightLines | number[] | No | Override the default highlighted lines for this tab |
Code Preview With Multiple Tabs
Open in
1<div className="p-4 border rounded-lg">
2 <h2 className="text-xl font-bold mb-4">Fights Counter</h2>
3 <p className="mb-2">Fight Club Fights Count: {count}</p>
4 <button
5 onClick={handleClick}
6 className="px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-600"
7 >
8 Increment
9 </button>
10</div>
11
Get beautiful, hand-crafted templates and components with Aceternity UI Pro
Professional, beautiful and elegant templates for your business. Get the best component packs and templates with Aceternity UI Pro.
Excellent communication and professionalism from the start and throughout. Happily and calmly accepted and entertained a few additional out-of-scope requests as well. Good open-...
Henrik Söderlund
Former CTO at Creme Digital