Loading…
Loading…
Displays formatted code with optional syntax highlighting and copy functionality.
The Code Block component renders preformatted source code with optional syntax highlighting, line numbers, a copy-to-clipboard button, and language identification. It is essential for developer documentation, technical blogs, API references, design-system sites, and any interface where code needs to be displayed or shared.
Code blocks differ from inline code (<code> within running text) in that they are block-level elements representing complete code snippets — functions, configuration files, terminal commands, or multi-line examples. The <pre> element preserves whitespace and line breaks, while the nested <code> element carries the language semantics.
When to use a Code Block:
When NOT to use a Code Block:
<code> inline<pre> without syntax highlightingTypography is critical for code readability. Use the Font Explorer to evaluate monospace typefaces — Fira Code, JetBrains Mono, and Source Code Pro are popular choices with programming ligature support. Verify that your code block's foreground colors against its background pass contrast checks with the Contrast Checker, especially for syntax-highlighted tokens.
| Variant | Purpose | Visual Treatment |
|---|---|---|
| Standard | General code display with syntax highlighting | Dark or light background, monospace font, optional line numbers |
| Terminal | Shell commands and CLI output | Dark background, $ or > prompt prefix, green/white text on black |
| Diff | Before/after code comparison | Red (removed) and green (added) line highlights with -/+ prefixes |
| Inline Editor | Editable code for playground/sandbox contexts | Editable contenteditable or <textarea> with live syntax highlighting |
| Collapsed | Long code hidden behind an expand trigger | Shows first N lines with "Show more" button; prevents scroll fatigue |
| Multi-file | Tabbed interface showing multiple files | Tab bar with filenames, shared container, single active panel |
| Theme | Background | Text Color | Use Case |
|---|---|---|---|
| Dark | #1e1e1e – #282c34 | #abb2bf – #d4d4d4 | Default for most dev docs. High contrast. |
| Light | #fafafa – #ffffff | #383a42 – #24292e | Matches light-mode UIs. GitHub-style. |
| High Contrast | #000000 | #ffffff with saturated tokens | Accessibility mode for low-vision users |
| Feature | Description |
|---|---|
| Line Numbers | Numbered gutter on the left. Disable for short snippets (< 5 lines). |
| Copy Button | One-click clipboard copy with confirmation feedback |
| Language Badge | Small label showing the language (e.g., "TypeScript", "bash") |
| Line Highlighting | Emphasize specific lines for teaching or code review |
| Word Wrap | Toggle between horizontal scroll and word wrapping |
| Property | Type | Default | Description |
|---|---|---|---|
language | string | 'text' | Programming language for syntax highlighting (e.g., 'typescript', 'python', 'bash') |
theme | 'dark' | 'light' | 'high-contrast' | 'dark' | Color theme for the code block |
showLineNumbers | boolean | true | Display line numbers in the gutter |
highlightLines | number[] | [] | Array of line numbers to visually emphasize |
showCopyButton | boolean | true | Show copy-to-clipboard button |
showLanguageBadge | boolean | true | Show language label in the header |
wordWrap | boolean | false | Wrap long lines instead of horizontal scrolling |
maxHeight | string | number | — | Maximum height before scrolling (e.g., '400px') |
fileName | string | — | File name to display in the header bar |
startLine | number | 1 | Starting line number for the gutter |
diff | boolean | false | Enable diff mode (lines prefixed with +/- are colored) |
children | string | — | The code content as a string |
Important: Always pass code content as a raw string, not as JSX children with embedded HTML. Syntax highlighters (Prism, Shiki, Highlight.js) expect plain text and will break on pre-parsed HTML entities.
| Token Category | Token Example | Code Block Usage |
|---|---|---|
| Color – Background | --color-code-bg (#1e1e1e) | Container background |
| Color – Text | --color-code-text (#d4d4d4) | Default unhighlighted text |
| Color – Gutter | --color-code-gutter (#858585) | Line number text color |
| Color – Highlight | --color-code-line-highlight (rgba(255,255,255,0.07)) | Highlighted line background |
| Color – Diff Add | --color-code-diff-add (rgba(40,167,69,0.15)) | Added line background in diff mode |
| Color – Diff Remove | --color-code-diff-remove (rgba(220,53,69,0.15)) | Removed line background in diff mode |
| Typography – Family | --font-family-mono | Monospace typeface. Choose with Font Explorer. |
| Typography – Size | --font-size-code (0.875rem) | Code text size — smaller than body to fit more content |
| Typography – Line Height | --line-height-code (1.6) | Line height for code readability |
| Spacing – Padding | --space-4 | Internal padding around code content |
| Border – Radius | --radius-md | Container corner rounding |
| Shadow | --shadow-sm | Optional subtle shadow for floating code blocks |
Most syntax highlighting libraries use these semantic categories:
| Token | Example Color (Dark) | Purpose |
|---|---|---|
keyword | #c678dd | const, function, if, return |
string | #98c379 | String literals |
number | #d19a66 | Numeric literals |
comment | #5c6370 | Comments — ⚠️ often fails contrast. Use Contrast Checker. |
function | #61afef | Function names and calls |
variable | #e06c75 | Variable names |
type | #e5c07b | Type annotations |
operator | #56b6c2 | Operators (=, +, =>) |
| State | Visual Treatment | Notes |
|---|---|---|
| Default | Rendered code with syntax highlighting | The standard resting state |
| Hover (Copy Button) | Copy button icon transitions from muted to full opacity | Button should be always visible, not appear on container hover — hidden buttons hurt discoverability |
| Copied | Copy icon changes to checkmark, tooltip shows "Copied!" | Auto-revert after 2 seconds |
| Loading | Skeleton shimmer matching code block dimensions | When code loads asynchronously or syntax highlighting is deferred |
| Error | Red border or banner indicating invalid/unparseable code | For live editors where code validation occurs |
| Collapsed | Shows first N lines with gradient fade-out and expand button | For long snippets. Button text: "Show all 142 lines" |
| Expanded | Full code visible with collapse button at bottom | "Show less" returns to collapsed state |
| Focused | Visible focus ring around container | When user tabs to the copy button or code region |
| Scrolling | Horizontal scrollbar visible for long lines | Fade gradient on right edge hints at overflowing content |
The copy button interaction should follow this sequence:
Code blocks present unique accessibility challenges around keyboard navigation, screen reader behavior, and visual contrast for syntax tokens.
Semantic Structure (WCAG 1.3.1 – Info and Relationships):
<pre><code class="language-xxx"> structure — the <pre> preserves whitespace, the <code> signals code contentrole="region" and aria-label="Code example in [language]" to the container for easy screen reader navigationaria-labelKeyboard Access (WCAG 2.1.1 – Keyboard):
<button>) and operable with Enter/Spacetabindex="0" so keyboard users can scroll with arrow keys — add role="region" and aria-label when doing soColor Contrast (WCAG 1.4.3 – Contrast Minimum):
Non-Text Contrast (WCAG 1.4.11 – Non-Text Contrast):
Resize and Reflow (WCAG 1.4.10 – Reflow):
Screen Reader Behavior:
user-select: none on the gutter) and should be hidden from screen readers (aria-hidden="true") to avoid "1 const 2 function 3 return" chaosaria-live="polite"Do:
src/App.tsx)Don't:
language="text" or language="bash"<!-- Standard code block with syntax highlighting (Prism.js) -->
<div class="code-block" role="region" aria-label="Code example in JavaScript">
<div class="code-block-header">
<span class="code-block-language">JavaScript</span>
<button class="code-block-copy" aria-label="Copy code">
<svg class="icon-clipboard" aria-hidden="true"><!-- clipboard icon --></svg>
</button>
</div>
<pre class="code-block-pre"><code class="language-javascript">function greet(name) {
return \`Hello, \${name}!\`;
}
console.log(greet('World'));</code></pre>
</div>
<!-- Terminal variant -->
<div class="code-block code-block--terminal" role="region" aria-label="Terminal command">
<div class="code-block-header">
<span class="code-block-dots" aria-hidden="true">
<span></span><span></span><span></span>
</span>
<span class="code-block-language">bash</span>
<button class="code-block-copy" aria-label="Copy command">
<svg class="icon-clipboard" aria-hidden="true"><!-- icon --></svg>
</button>
</div>
<pre class="code-block-pre"><code class="language-bash">npm install @stellae/components
npx stellae init</code></pre>
</div>
<!-- Diff variant -->
<div class="code-block code-block--diff" role="region" aria-label="Code diff">
<pre class="code-block-pre"><code class="language-diff">- const color = 'red';
+ const color = 'blue';
const size = 'medium';</code></pre>
</div>
<style>
.code-block {
border-radius: 8px;
overflow: hidden;
background: #1e1e1e;
font-family: 'Fira Code', 'Consolas', monospace;
font-size: 0.875rem;
line-height: 1.6;
}
.code-block-header {
display: flex;
align-items: center;
justify-content: space-between;
padding: 8px 16px;
background: rgba(255, 255, 255, 0.05);
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
}
.code-block-language {
font-size: 0.75rem;
color: #858585;
text-transform: uppercase;
letter-spacing: 0.05em;
}
.code-block-copy {
background: none;
border: none;
color: #858585;
cursor: pointer;
padding: 4px;
border-radius: 4px;
transition: color 0.15s, background 0.15s;
}
.code-block-copy:hover {
color: #d4d4d4;
background: rgba(255, 255, 255, 0.1);
}
.code-block-pre {
margin: 0;
padding: 16px;
overflow-x: auto;
color: #d4d4d4;
}
.code-block--terminal {
background: #0d1117;
}
.code-block-dots span {
display: inline-block;
width: 12px;
height: 12px;
border-radius: 50%;
margin-right: 6px;
}
.code-block-dots span:nth-child(1) { background: #ff5f56; }
.code-block-dots span:nth-child(2) { background: #ffbd2e; }
.code-block-dots span:nth-child(3) { background: #27c93f; }
</style>import React, { useState, useCallback } from 'react';
import styles from './CodeBlock.module.css';
import clsx from 'clsx';
interface CodeBlockProps {
language?: string;
theme?: 'dark' | 'light' | 'high-contrast';
showLineNumbers?: boolean;
highlightLines?: number[];
showCopyButton?: boolean;
showLanguageBadge?: boolean;
wordWrap?: boolean;
maxHeight?: string | number;
fileName?: string;
startLine?: number;
diff?: boolean;
children: string;
}
export function CodeBlock({
language = 'text',
theme = 'dark',
showLineNumbers = true,
highlightLines = [],
showCopyButton = true,
showLanguageBadge = true,
wordWrap = false,
maxHeight,
fileName,
startLine = 1,
diff = false,
children,
}: CodeBlockProps) {
const [copied, setCopied] = useState(false);
const lines = children.split('\n');
const handleCopy = useCallback(async () => {
try {
await navigator.clipboard.writeText(children);
setCopied(true);
setTimeout(() => setCopied(false), 2000);
} catch {
/* fallback: textarea select + execCommand */
}
}, [children]);
return (
<div
className={clsx(styles.root, styles[theme], { [styles.diff]: diff })}
role="region"
aria-label={`Code example in ${language}`}
>
<div className={styles.header}>
{fileName && <span className={styles.fileName}>{fileName}</span>}
{showLanguageBadge && !fileName && (
<span className={styles.language}>{language}</span>
)}
{showCopyButton && (
<button
className={styles.copyButton}
onClick={handleCopy}
aria-label={copied ? 'Code copied to clipboard' : 'Copy code'}
>
{copied ? '✓ Copied' : 'Copy'}
</button>
)}
</div>
<pre
className={styles.pre}
style={{
maxHeight: maxHeight,
whiteSpace: wordWrap ? 'pre-wrap' : 'pre',
}}
>
<code className={`language-${language}`}>
{lines.map((line, i) => {
const lineNum = startLine + i;
const isHighlighted = highlightLines.includes(lineNum);
return (
<div
key={i}
className={clsx(styles.line, {
[styles.highlighted]: isHighlighted,
[styles.added]: diff && line.startsWith('+'),
[styles.removed]: diff && line.startsWith('-'),
})}
>
{showLineNumbers && (
<span className={styles.lineNumber} aria-hidden="true">
{lineNum}
</span>
)}
<span className={styles.lineContent}>{line}</span>
</div>
);
})}
</code>
</pre>
</div>
);
}Material Design (MUI) does not include a Code Block component in its core library. Teams typically use third-party libraries like react-syntax-highlighter (with Prism or Highlight.js backends) wrapped in MUI's <Paper> for consistent theming. MUI's documentation site itself uses a custom code block with Prism.js, copy buttons, and language tabs — but this component is not exported for external use.
Ant Design does not offer a Code Block component. Ant's documentation site uses a custom implementation with Prism.js. For projects using Ant Design, the recommendation is to integrate prism-react-renderer or react-syntax-highlighter and style them to match Ant's visual language using its token system.
Chakra UI includes no dedicated Code Block. The <Code> component handles inline code (colored background, monospace font). For block-level code, Chakra recommends composing <Box as="pre"> with a syntax highlighting library. The @chakra-ui/prose plugin styles native <pre><code> elements within prose content.
Bootstrap provides basic <pre> and <code> styling with scrollable overflow and monospace font. No syntax highlighting, copy button, or line numbers are included. Bootstrap's $code-color and $pre-color Sass variables control text color. For production code blocks, Bootstrap users add Prism.js or Highlight.js independently.
Apple Human Interface Guidelines addresses code display in the context of developer documentation (Xcode, Swift Playgrounds). Apple uses San Francisco Mono as its system monospace font with a proprietary syntax highlighting theme. In SwiftUI, there is no native code block view — developers use Text with attributed strings or integrate a WKWebView with a JavaScript-based highlighter.
Tailwind CSS styles <pre><code> blocks via the @tailwindcss/typography plugin: monospace font, rounded background, horizontal overflow scroll. Tailwind's utility classes enable quick custom code blocks: bg-gray-900 text-gray-100 rounded-lg p-4 font-mono text-sm overflow-x-auto. For syntax highlighting, the community pairs Tailwind with Shiki (build-time highlighting) or Prism.js. Use the Font Explorer to compare monospace typefaces like Fira Code, JetBrains Mono, and IBM Plex Mono.