Code

Syntax-highlighted code with a copy button, a filename bar, and a package-manager tab strip

greet.ts
1export function greet(name: string) {
2 const greeting = `Hello, ${name}!`;
3 console.log(greeting);
4 return greeting;
5}
import { Code } from "@/components/ui/code";
const SAMPLE = `export function greet(name: string) {
const greeting = \`Hello, \${name}!\`;
console.log(greeting);
return greeting;
}`;
export default function CodeDemo() {
return (
<div className="w-full max-w-[520px]">
<Code filename="greet.ts" language="typescript" code={SAMPLE} />
</div>
);
}

Installation

npx shadcn@latest add https://www.deltacomponents.dev/r/code.json

Usage

1import { Code } from "@/components/ui/code";
2
3<Code language="tsx" code={source} />

Basic

1export function greet(name: string) {
2 const greeting = `Hello, ${name}!`;
3 console.log(greeting);
4 return greeting;
5}
import { Code } from "@/components/ui/code";
const SAMPLE = `export function greet(name: string) {
const greeting = \`Hello, \${name}!\`;
console.log(greeting);
return greeting;
}`;
export default function CodeBasic() {
return (
<div className="w-full max-w-[520px]">
<Code language="typescript" code={SAMPLE} />
</div>
);
}

Package managers

npx shadcn@latest add code
import { Code } from "@/components/ui/code";
export default function CodePackageManagers() {
return (
<div className="w-full max-w-[520px]">
<Code code={"```npx\nshadcn@latest add code\n```"} />
</div>
);
}

Custom theme

word_count.rs
1use std::collections::HashMap;
2
3/// Counts how many times each word appears.
4pub fn word_counts(text: &str) -> HashMap<String, usize> {
5 let mut counts: HashMap<String, usize> = HashMap::new();
6
7 for word in text.split_whitespace() {
8 let key = word.trim_matches(|c: char| !c.is_alphanumeric());
9 if key.is_empty() {
10 continue;
11 }
12 *counts.entry(key.to_lowercase()).or_insert(0) += 1;
13 }
14
15 counts
16}
17
18#[cfg(test)]
19mod tests {
20 use super::*;
21
22 #[test]
23 fn counts_repeats() {
24 let counts = word_counts("the cat the hat");
25 assert_eq!(counts["the"], 2);
26 }
27}
import { Code } from "@/components/ui/code";
import { PATRICK_DARK } from "@/lib/docs/code-themes";
const RUST_SAMPLE = `use std::collections::HashMap;
/// Counts how many times each word appears.
pub fn word_counts(text: &str) -> HashMap<String, usize> {
let mut counts: HashMap<String, usize> = HashMap::new();
for word in text.split_whitespace() {
let key = word.trim_matches(|c: char| !c.is_alphanumeric());
if key.is_empty() {
continue;
}
*counts.entry(key.to_lowercase()).or_insert(0) += 1;
}
counts
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn counts_repeats() {
let counts = word_counts("the cat the hat");
assert_eq!(counts["the"], 2);
}
}`;
export default function CodeCustomTheme() {
return (
<div className="w-full max-w-[520px]">
<Code
filename="word_count.rs"
language="rust"
theme={PATRICK_DARK}
code={RUST_SAMPLE}
/>

Adaptive theme

A { light, dark } pair follows the site theme instead of pinning one palette. Turn useThemeBackground off to keep the page's own card surface and take only the token colours.

badge.tsx
1type BadgeProps = {
2 label: string;
3 tone?: "neutral" | "positive";
4};
5
6export function Badge({ label, tone = "neutral" }: BadgeProps) {
7 const count = label.length;
8 return (
9 <span className={tone === "positive" ? "ok" : "muted"} data-count={count}>
10 {label}
11 </span>
12 );
13}
import { useState } from "react";
import { Code } from "@/components/ui/code";
import { Button } from "@/components/ui/button";
import { PIERRE } from "@/lib/docs/code-themes";
const SAMPLE = `type BadgeProps = {
label: string;
tone?: "neutral" | "positive";
};
export function Badge({ label, tone = "neutral" }: BadgeProps) {
const count = label.length;
return (
<span className={tone === "positive" ? "ok" : "muted"} data-count={count}>
{label}
</span>
);
}`;
export default function CodeAdaptiveTheme() {
const [useThemeBackground, setUseThemeBackground] = useState(true);
return (
<div className="flex w-full max-w-[520px] flex-col gap-3">
<Button
size="compact"
variant="secondary"
className="self-start"
onClick={() => setUseThemeBackground((on) => !on)}
>
useThemeBackground: {useThemeBackground ? "on" : "off"}
</Button>
<Code
filename="badge.tsx"
language="tsx"
adaptiveTheme={PIERRE}
useThemeBackground={useThemeBackground}
code={SAMPLE}
/>
</div>

Expandable

hooks.ts
1import { useEffect, useState } from "react";
2
3export function useDebounced<T>(value: T, delay = 200): T {
4 const [debounced, setDebounced] = useState(value);
5
6 useEffect(() => {
7 const timer = setTimeout(() => setDebounced(value), delay);
8 return () => clearTimeout(timer);
9 }, [value, delay]);
10
11 return debounced;
12}
13
14export function useOnline(): boolean {
15 const [online, setOnline] = useState(true);
16
17 useEffect(() => {
18 const update = () => setOnline(navigator.onLine);
19 update();
20 window.addEventListener("online", update);
21 window.addEventListener("offline", update);
22 return () => {
23 window.removeEventListener("online", update);
24 window.removeEventListener("offline", update);
25 };
26 }, []);
27
28 return online;
29}
import { Code } from "@/components/ui/code";
const LONG_SAMPLE = `import { useEffect, useState } from "react";
export function useDebounced<T>(value: T, delay = 200): T {
const [debounced, setDebounced] = useState(value);
useEffect(() => {
const timer = setTimeout(() => setDebounced(value), delay);
return () => clearTimeout(timer);
}, [value, delay]);
return debounced;
}
export function useOnline(): boolean {
const [online, setOnline] = useState(true);
useEffect(() => {
const update = () => setOnline(navigator.onLine);
update();
window.addEventListener("online", update);
window.addEventListener("offline", update);
return () => {
window.removeEventListener("online", update);
window.removeEventListener("offline", update);
};
}, []);
return online;
}`;
export default function CodeExpandable() {
return (
<div className="w-full max-w-[520px]">
<Code
filename="hooks.ts"
language="typescript"
code={LONG_SAMPLE}
expandable

Override Styling

className lands on the block's root and is merged with tailwind-merge, so a utility that conflicts with a default replaces it instead of piling on top of it. The type size is set on the root rather than on the <pre>, which is what lets one text-* resize the code, the line-number gutter and the filename bar together — the row height follows the type with it.

use-theme.ts
1import { useEffect, useState } from "react";
2
3export function useTheme() {
4 const [theme, setTheme] = useState("light");
5
6 useEffect(() => {
7 document.body.dataset.theme = theme;
8 }, [theme]);
9
10 return { theme, setTheme };
11}
import { Code } from "@/components/ui/code";
const SAMPLE = `import { useEffect, useState } from "react";
export function useTheme() {
const [theme, setTheme] = useState("light");
useEffect(() => {
document.body.dataset.theme = theme;
}, [theme]);
return { theme, setTheme };
}`;
// A neobrutalist block, three overrides deep. The root: a 2px border and a
// hard 4px offset shadow with no blur — black on the light page, white on the
// dark one, the slab inverting with the page. `border-2` replaces the default
// hairline rather than stacking on it, because the class is merged; the type
// stays at `text-base`. The title bar, through its slot: a flat pastel with a
// hard rule under it (black on both pages) and near-black text. The filename
// and its icon normally sit at 60% of the bar's colour, which greys them over
// a pastel, so that slot is brought to full strength. The gutter, through its
// slot: pulled in.
const OVERRIDES = [
"rounded-xs border-2 border-neutral-950 text-base shadow-[4px_4px_0_0_#0a0a0a]",
"dark:border-[#88aaee] dark:shadow-[4px_4px_0_0_#fff]",
"**:data-[slot=code-header]:border-b-2 **:data-[slot=code-header]:border-neutral-950 **:data-[slot=code-header]:bg-[#88aaee] **:data-[slot=code-header]:text-neutral-950 **:data-[slot=code-header]:py-3",
"**:data-[slot=code-filename]:opacity-100",
"**:data-[slot=code-gutter]:w-10 **:data-[slot=code-gutter]:pr-4 **:data-[slot=code-gutter]:pl-2",
].join(" ");
export default function CodeOverrideStyling() {
return (
// Room on the right and below for the shadow, which sits outside the box.
<div className="w-full max-w-[560px] py-2 pr-1 pb-3">
<Code
language="typescript"
filename="use-theme.ts"
code={SAMPLE}
className={OVERRIDES}

Every inner part carries a data-slot, so it is reachable from that same className and needs no prop of its own. The chrome inside a part carries no colour of its own — the filename and the copy glyph sit at 60% of whatever colour the header inherits — so recolouring the bar carries both with it:

A theme or adaptiveTheme paints the header and code surface from inline style, which no class can outrank. Turn useThemeBackground off to keep those surfaces on the page's card token and colour them yourself.

API Reference