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";23<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
Custom theme
1use std::collections::HashMap;23/// 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();67 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 }1415 counts16}1718#[cfg(test)]19mod tests {20 use super::*;2122 #[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]"><Codefilename="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.
1type BadgeProps = {2 label: string;3 tone?: "neutral" | "positive";4};56export 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"><Buttonsize="compact"variant="secondary"className="self-start"onClick={() => setUseThemeBackground((on) => !on)}>useThemeBackground: {useThemeBackground ? "on" : "off"}</Button><Codefilename="badge.tsx"language="tsx"adaptiveTheme={PIERRE}useThemeBackground={useThemeBackground}code={SAMPLE}/></div>
Expandable
1import { useEffect, useState } from "react";23export function useDebounced<T>(value: T, delay = 200): T {4 const [debounced, setDebounced] = useState(value);56 useEffect(() => {7 const timer = setTimeout(() => setDebounced(value), delay);8 return () => clearTimeout(timer);9 }, [value, delay]);1011 return debounced;12}1314export function useOnline(): boolean {15 const [online, setOnline] = useState(true);1617 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 }, []);2728 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]"><Codefilename="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.
1import { useEffect, useState } from "react";23export function useTheme() {4 const [theme, setTheme] = useState("light");56 useEffect(() => {7 document.body.dataset.theme = theme;8 }, [theme]);910 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"><Codelanguage="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.