All components
Code Block
data-displayAceternity UI component.
responsive · 560px
Install
Same command in any shadcn project — React (Vite/CRA), Next.js, Remix, Astro, and more:
$
npx shadcn@latest add https://your-domain/r/code-block.jsonUsage
"use client";
import { CodeBlock } from "@/registry/aceternity-ui/code-block";
const singleCode = `import { useState } from "react";
export function Counter() {
const [count, setCount] = useState(0);
return (
<div className="flex items-center gap-4 p-6">
<button onClick={() => setCount(c => c - 1)}>-</button>
<span className="text-2xl font-mono">{count}</span>
<button onClick={() => setCount(c => c + 1)}>+</button>
</div>
);
}`;
const tabsCode = [
{
name: "counter.tsx",
language: "tsx",
code: singleCode,
highlightLines: [6, 7, 8, 9],
},
{
name: "styles.css",
language: "css",
code: `.counter {
display: flex;
align-items: center;
gap: 1rem;
padding: 1.5rem;
}
.counter button {
width: 2rem;
height: 2rem;
border-radius: 0.25rem;
border: 1px solid #333;
background: transparent;
cursor: pointer;
}`,
},
];
export default function Demo() {
return (
<div className="w-full max-w-2xl mx-auto px-4 py-6 space-y-6">
<CodeBlock
language="tsx"
filename="counter.tsx"
code={singleCode}
highlightLines={[6, 7, 8, 9]}
/>
<CodeBlock
language="tsx"
filename="counter.tsx"
tabs={tabsCode}
/>
</div>
);
}Component source
"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>
);
};Dependencies
@tabler/icons-reactreact-syntax-highlighter
Source: Aceternity UI