Skip to main content

Overview

This guide demonstrates how to build a flexible, reusable CodeBlock component that you can use throughout your React application. We’ll cover basic usage, prop handling, and adding useful features like language labels and copy-to-clipboard functionality.

Basic CodeBlock Component

Start with a simple component that accepts code as a prop:
CodeBlock.jsx
import { highlight } from 'code-syntactic-sugar';

export function CodeBlock({ code, language = 'javascript' }) {
  const highlightedCode = highlight(code);

  return (
    <div className="code-block">
      <pre>
        <code>{highlightedCode}</code>
      </pre>
    </div>
  );
}

Usage

App.jsx
import { CodeBlock } from './CodeBlock';

function App() {
  const exampleCode = `
const greeting = "Hello, World!";
console.log(greeting);
  `.trim();

  return (
    <div>
      <h1>Code Example</h1>
      <CodeBlock code={exampleCode} />
    </div>
  );
}

Enhanced CodeBlock with Features

Let’s enhance the component with language labels, copy functionality, and line highlighting:
import { highlight } from 'code-syntactic-sugar';
import { useState } from 'react';
import './CodeBlock.css';

export function CodeBlock({ 
  code, 
  language = 'javascript',
  showLineNumbers = false,
  highlightedLines = [],
  addedLines = [],
  removedLines = [],
  showCopyButton = true,
  fileName
}) {
  const [copied, setCopied] = useState(false);

  const highlightedCode = highlight(code, {
    modifiers: {
      highlightedLines,
      addedLines,
      removedLines,
    },
  });

  const handleCopy = async () => {
    await navigator.clipboard.writeText(code);
    setCopied(true);
    setTimeout(() => setCopied(false), 2000);
  };

  return (
    <div className="code-block">
      {(fileName || language || showCopyButton) && (
        <div className="code-block-header">
          <div className="code-block-info">
            {fileName && <span className="file-name">{fileName}</span>}
            {!fileName && language && (
              <span className="language-label">{language}</span>
            )}
          </div>
          {showCopyButton && (
            <button 
              className="copy-button" 
              onClick={handleCopy}
              aria-label="Copy code to clipboard"
            >
              {copied ? 'Copied!' : 'Copy'}
            </button>
          )}
        </div>
      )}
      <pre className={showLineNumbers ? 'with-line-numbers' : ''}>
        <code>{highlightedCode}</code>
      </pre>
    </div>
  );
}

Usage Examples

import { CodeBlock } from './CodeBlock';

function Example() {
  const code = `
function fibonacci(n) {
if (n <= 1) return n;
return fibonacci(n - 1) + fibonacci(n - 2);
}
  `.trim();

  return <CodeBlock code={code} language="javascript" />;
}
The highlight function returns React elements directly, so you don’t need to use dangerouslySetInnerHTML. This makes it safer and more React-friendly than other syntax highlighting libraries.

TypeScript Support

For TypeScript projects, create a typed version of the component:
CodeBlock.tsx
import { highlight, CodeSyntacticSugarConfig } from 'code-syntactic-sugar';
import { useState } from 'react';
import './CodeBlock.css';

interface CodeBlockProps {
  code: string;
  language?: string;
  showLineNumbers?: boolean;
  highlightedLines?: number[];
  addedLines?: number[];
  removedLines?: number[];
  showCopyButton?: boolean;
  fileName?: string;
}

export function CodeBlock({ 
  code, 
  language = 'javascript',
  showLineNumbers = false,
  highlightedLines = [],
  addedLines = [],
  removedLines = [],
  showCopyButton = true,
  fileName
}: CodeBlockProps) {
  const [copied, setCopied] = useState<boolean>(false);

  const config: CodeSyntacticSugarConfig = {
    modifiers: {
      highlightedLines: highlightedLines.length > 0 
        ? (highlightedLines as [number, ...number[]]) 
        : undefined,
      addedLines: addedLines.length > 0 
        ? (addedLines as [number, ...number[]]) 
        : undefined,
      removedLines: removedLines.length > 0 
        ? (removedLines as [number, ...number[]]) 
        : undefined,
    },
  };

  const highlightedCode = highlight(code, config);

  const handleCopy = async () => {
    await navigator.clipboard.writeText(code);
    setCopied(true);
    setTimeout(() => setCopied(false), 2000);
  };

  return (
    <div className="code-block">
      {(fileName || language || showCopyButton) && (
        <div className="code-block-header">
          <div className="code-block-info">
            {fileName && <span className="file-name">{fileName}</span>}
            {!fileName && language && (
              <span className="language-label">{language}</span>
            )}
          </div>
          {showCopyButton && (
            <button 
              className="copy-button" 
              onClick={handleCopy}
              aria-label="Copy code to clipboard"
            >
              {copied ? 'Copied!' : 'Copy'}
            </button>
          )}
        </div>
      )}
      <pre className={showLineNumbers ? 'with-line-numbers' : ''}>
        <code>{highlightedCode}</code>
      </pre>
    </div>
  );
}
When using TypeScript, the modifiers configuration requires non-empty tuples. Always check if your arrays have elements before passing them to the config.

Best Practices

Performance Optimization

For code blocks that don’t change frequently, consider memoizing the highlighted output:
import { useMemo } from 'react';
import { highlight } from 'code-syntactic-sugar';

function CodeBlock({ code, highlightedLines }) {
  const highlightedCode = useMemo(
    () => highlight(code, { 
      modifiers: { highlightedLines } 
    }),
    [code, highlightedLines]
  );

  return (
    <pre>
      <code>{highlightedCode}</code>
    </pre>
  );
}

Accessibility

Always include proper ARIA labels and semantic HTML:
  • Use <pre> and <code> tags for semantic structure
  • Add aria-label to interactive buttons
  • Ensure sufficient color contrast for syntax highlighting
  • Consider adding a “language” announcement for screen readers

Styling Considerations

  • Use CSS custom properties for theme flexibility
  • Ensure line numbers don’t interfere with text selection
  • Make the copy button visible on hover for better UX
  • Test with different font families and sizes

Build docs developers (and LLMs) love