Skip to content

Code Block

Displays formatted code with optional syntax highlighting and copy functionality.

  • Code Snippet
  • Syntax Highlight

Overview

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:

  • To display source code examples in documentation
  • For terminal/CLI commands or shell output
  • To show configuration files (JSON, YAML, TOML)
  • For API request/response payloads
  • To present diff comparisons between code versions

When NOT to use a Code Block:

  • For inline code references within a sentence — use <code> inline
  • For user-generated content that happens to contain code — consider a sandboxed preview
  • For poetry or preformatted non-code text — use <pre> without syntax highlighting
  • For tabular data — use a Table

Typography 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.

Variants

Common Code Block Variants

VariantPurposeVisual Treatment
StandardGeneral code display with syntax highlightingDark or light background, monospace font, optional line numbers
TerminalShell commands and CLI outputDark background, $ or > prompt prefix, green/white text on black
DiffBefore/after code comparisonRed (removed) and green (added) line highlights with -/+ prefixes
Inline EditorEditable code for playground/sandbox contextsEditable contenteditable or <textarea> with live syntax highlighting
CollapsedLong code hidden behind an expand triggerShows first N lines with "Show more" button; prevents scroll fatigue
Multi-fileTabbed interface showing multiple filesTab bar with filenames, shared container, single active panel

Theme Variants

ThemeBackgroundText ColorUse Case
Dark#1e1e1e – #282c34#abb2bf – #d4d4d4Default for most dev docs. High contrast.
Light#fafafa – #ffffff#383a42 – #24292eMatches light-mode UIs. GitHub-style.
High Contrast#000000#ffffff with saturated tokensAccessibility mode for low-vision users

Feature Toggles

FeatureDescription
Line NumbersNumbered gutter on the left. Disable for short snippets (< 5 lines).
Copy ButtonOne-click clipboard copy with confirmation feedback
Language BadgeSmall label showing the language (e.g., "TypeScript", "bash")
Line HighlightingEmphasize specific lines for teaching or code review
Word WrapToggle between horizontal scroll and word wrapping

Properties

Code Block Properties

PropertyTypeDefaultDescription
languagestring'text'Programming language for syntax highlighting (e.g., 'typescript', 'python', 'bash')
theme'dark' | 'light' | 'high-contrast''dark'Color theme for the code block
showLineNumbersbooleantrueDisplay line numbers in the gutter
highlightLinesnumber[][]Array of line numbers to visually emphasize
showCopyButtonbooleantrueShow copy-to-clipboard button
showLanguageBadgebooleantrueShow language label in the header
wordWrapbooleanfalseWrap long lines instead of horizontal scrolling
maxHeightstring | number—Maximum height before scrolling (e.g., '400px')
fileNamestring—File name to display in the header bar
startLinenumber1Starting line number for the gutter
diffbooleanfalseEnable diff mode (lines prefixed with +/- are colored)
childrenstring—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 Mappings

Design Token Mappings

Token CategoryToken ExampleCode 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-monoMonospace 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-4Internal padding around code content
Border – Radius--radius-mdContainer corner rounding
Shadow--shadow-smOptional subtle shadow for floating code blocks

Syntax Highlighting Token Map

Most syntax highlighting libraries use these semantic categories:

TokenExample Color (Dark)Purpose
keyword#c678ddconst, function, if, return
string#98c379String literals
number#d19a66Numeric literals
comment#5c6370Comments — ⚠️ often fails contrast. Use Contrast Checker.
function#61afefFunction names and calls
variable#e06c75Variable names
type#e5c07bType annotations
operator#56b6c2Operators (=, +, =>)

States

Code Block States

StateVisual TreatmentNotes
DefaultRendered code with syntax highlightingThe standard resting state
Hover (Copy Button)Copy button icon transitions from muted to full opacityButton should be always visible, not appear on container hover — hidden buttons hurt discoverability
CopiedCopy icon changes to checkmark, tooltip shows "Copied!"Auto-revert after 2 seconds
LoadingSkeleton shimmer matching code block dimensionsWhen code loads asynchronously or syntax highlighting is deferred
ErrorRed border or banner indicating invalid/unparseable codeFor live editors where code validation occurs
CollapsedShows first N lines with gradient fade-out and expand buttonFor long snippets. Button text: "Show all 142 lines"
ExpandedFull code visible with collapse button at bottom"Show less" returns to collapsed state
FocusedVisible focus ring around containerWhen user tabs to the copy button or code region
ScrollingHorizontal scrollbar visible for long linesFade gradient on right edge hints at overflowing content

Copy Button Micro-interaction

The copy button interaction should follow this sequence:

  1. Idle: Clipboard icon, muted color
  2. Hover: Icon brightens, optional tooltip "Copy code"
  3. Click: Icon transitions to checkmark, text "Copied!" appears
  4. Confirmation hold: Checkmark visible for 2 seconds
  5. Reset: Smooth transition back to clipboard icon

Accessibility

Accessibility

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):

  • Use <pre><code class="language-xxx"> structure — the <pre> preserves whitespace, the <code> signals code content
  • Add role="region" and aria-label="Code example in [language]" to the container for easy screen reader navigation
  • The language badge provides visual context — ensure it's also available programmatically via the aria-label

Keyboard Access (WCAG 2.1.1 – Keyboard):

  • The copy button must be keyboard-focusable (<button>) and operable with Enter/Space
  • If the code block is horizontally scrollable, the container needs tabindex="0" so keyboard users can scroll with arrow keys — add role="region" and aria-label when doing so
  • For tabbed multi-file code blocks, implement the Tabs keyboard pattern (Arrow keys between tabs, Tab to content)

Color Contrast (WCAG 1.4.3 – Contrast Minimum):

  • Every syntax token color must achieve 4.5:1 contrast against the code block background. Use the Contrast Checker.
  • Comments are the most common failure — the typically gray/muted comment color often falls below 4.5:1 on both dark and light backgrounds. Test this token explicitly.
  • Line numbers (gutter) carry information and must meet 4.5:1 contrast
  • Highlighted line backgrounds must not reduce token contrast below minimums

Non-Text Contrast (WCAG 1.4.11 – Non-Text Contrast):

  • The copy button icon must have 3:1 contrast against its background
  • Line highlighting background must be distinct enough (3:1 against default background) to be perceivable

Resize and Reflow (WCAG 1.4.10 – Reflow):

  • Code blocks are exempt from reflow requirements (WCAG allows horizontal scrolling for preformatted content)
  • However, provide a word-wrap toggle as a user preference for those who prefer it
  • At 400% zoom, the code block container should remain usable — avoid fixed widths

Screen Reader Behavior:

  • Screen readers read code blocks as continuous text. Syntax highlighting is purely visual and carries no semantic weight — this is acceptable
  • Line numbers should not be selectable (use CSS user-select: none on the gutter) and should be hidden from screen readers (aria-hidden="true") to avoid "1 const 2 function 3 return" chaos
  • The copy button should announce "Copy code" and the confirmation "Code copied to clipboard" via aria-live="polite"

Usage Guidelines

Usage Guidelines

Do:

  • Specify the language accurately — wrong syntax highlighting is worse than none
  • Include a copy button for any code users are expected to use
  • Use line highlighting to draw attention to important lines in educational content
  • Show file names when the code's location matters (e.g., src/App.tsx)
  • Keep code examples concise — show the minimum needed to illustrate the concept
  • Provide both dark and light theme options if your site supports both modes
  • Use a monospace font with programming ligatures for improved readability. Explore options with the Font Explorer.

Don't:

  • Don't use syntax highlighting for plain text, logs, or terminal output — use language="text" or language="bash"
  • Don't auto-collapse short code blocks (under ~15 lines) — the expand/collapse adds unnecessary friction
  • Don't disable text selection on code — users need to select and copy portions
  • Don't apply word wrap by default — it breaks visual line structure that developers rely on for reading code
  • Don't use code blocks for JSON/YAML configuration that users need to edit — provide a form interface with a "View as code" toggle
  • Don't forget to escape HTML entities if rendering code server-side without a proper highlighter

Code Snippets

html
<!-- 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>
tsx
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>
  );
}

Design Systems

Design System Implementations

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.

TypographyCode SnippetSyntax Highlight