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:
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
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:
CodeBlock.jsx
CodeBlock.css
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
Basic Example
With Line Numbers
With Highlighting
With File Name
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" /> ;
}
import { CodeBlock } from './CodeBlock' ;
function Example () {
const code = `
import React from 'react';
import { createRoot } from 'react-dom/client';
function App() {
return <h1>Hello, React!</h1>;
}
const root = createRoot(document.getElementById('root'));
root.render(<App />);
` . trim ();
return (
< CodeBlock
code = { code }
language = "jsx"
showLineNumbers = { true }
/>
);
}
import { CodeBlock } from './CodeBlock' ;
function Example () {
const code = `
const oldValue = 10;
const newValue = 20;
const result = newValue * 2;
console.log('Result:', result);
console.log('Calculation complete');
` . trim ();
return (
< CodeBlock
code = { code }
fileName = "calculations.js"
highlightedLines = { [ 3 , 4 ] }
addedLines = { [ 2 ] }
removedLines = { [ 1 ] }
showLineNumbers = { true }
/>
);
}
import { CodeBlock } from './CodeBlock' ;
function Example () {
const code = `
export default function Button({ children, onClick }) {
return (
<button
onClick={onClick}
className="px-4 py-2 bg-blue-500 text-white rounded"
>
{children}
</button>
);
}
` . trim ();
return (
< CodeBlock
code = { code }
fileName = "components/Button.jsx"
showLineNumbers = { true }
/>
);
}
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:
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