react-syntax-highlighter
React component that turns source code into a highlighted React element tree using either highlight.js through lowlight or Prism through refractor. It ships JavaScript theme objects, line numbering, per-line props, custom renderers, and full, light, and asynchronous builds. Unlike highlighters that inject an HTML string after mount, it builds React nodes from a syntax tree, so updates stay inside React's rendering model and do not require dangerouslySetInnerHTML.
A capable choice for interactive React code blocks, especially when per-line rendering matters. Do not accept the default full bundle casually; most products should use a light build or highlight code before it reaches the browser.
Use it if
- You need highlighted code blocks whose lines can receive React props for diffs, selections, annotations, or click handling
- You want a choice between highlight.js language detection and Prism grammar coverage behind nearly the same component API
- You need inline JavaScript themes and do not want to load a separate syntax-highlighting stylesheet
- You will use a light build and explicitly register the few languages your product actually displays
- You only render static Markdown at build time: a rehype or Shiki pipeline can emit finished HTML without shipping a React parser and language grammars to every reader
- You would import the default full build in a performance-sensitive page: Bundlephobia reports about 520.2 KB gzipped for version 16.1.1, and the README itself warns that the normal build has a fairly large footprint
- You want first-party TypeScript declarations in the same package: the installation section still tells TypeScript users to add the separate @types/react-syntax-highlighter package
- You need React Native: the README directs those users to the separate react-native-syntax-highlighter project
- You need highlighting to appear immediately with an asynchronous build: the README says code initially renders with line numbers but without highlighting while dynamic chunks load
Setup reality
The basic install is one runtime package, but a TypeScript project is expected to add @types/react-syntax-highlighter separately. The default export uses highlight.js; JSX and other Prism-oriented grammars usually mean importing Prism as the component and a theme from dist/esm/styles/prism. That path choice matters because highlight.js styles and Prism styles are not interchangeable. The normal build includes a broad language set and measured at about 520.2 KB gzipped in the registry size check, so production apps should usually choose Light or PrismLight, import each grammar from dist/esm/languages, and call registerLanguage before rendering. Light builds do not include a default theme. Async variants reduce initial work but require a bundler that supports dynamic import, and the README says code appears temporarily without highlighting while language chunks load. Line-level styling also has a hidden switch: lineProps does nothing useful until wrapLines is true. Setting useInlineStyles to false changes output to class names, which means you must supply compatible CSS yourself. The package requires Node 16.20.2 or newer for installation tooling and has React as a peer dependency, while React Native needs a different package.
Patterns
Render a highlight.js code blockhighlight-javascript
import SyntaxHighlighter from 'react-syntax-highlighter';
import { docco } from 'react-syntax-highlighter/dist/esm/styles/hljs';
export function CodeBlock({ code }) {
return (
<SyntaxHighlighter language="javascript" style={docco}>
{code}
</SyntaxHighlighter>
);
}The default component uses highlight.js through lowlight, so import a theme from the hljs style directory.
Use Prism for JSXhighlight-jsx-with-prism
import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter';
import { vscDarkPlus } from 'react-syntax-highlighter/dist/esm/styles/prism';
<SyntaxHighlighter language="jsx" style={vscDarkPlus}>
{source}
</SyntaxHighlighter>Prism themes must come from the prism style directory; mixing a Prism component with an hljs theme produces the wrong token keys.
Keep the bundle focused with Lightregister-light-language
import { Light as SyntaxHighlighter } from 'react-syntax-highlighter';
import javascript from 'react-syntax-highlighter/dist/esm/languages/hljs/javascript';
import docco from 'react-syntax-highlighter/dist/esm/styles/hljs/docco';
SyntaxHighlighter.registerLanguage('javascript', javascript);
export const Block = ({ code }) => (
<SyntaxHighlighter language="javascript" style={docco}>{code}</SyntaxHighlighter>
);Light has no default style or automatic grammar catalog; every used language must be imported and registered.
Register JSX with PrismLightregister-prism-language
import { PrismLight as SyntaxHighlighter } from 'react-syntax-highlighter';
import jsx from 'react-syntax-highlighter/dist/esm/languages/prism/jsx';
import prism from 'react-syntax-highlighter/dist/esm/styles/prism/prism';
SyntaxHighlighter.registerLanguage('jsx', jsx);
<SyntaxHighlighter language="jsx" style={prism}>{source}</SyntaxHighlighter>Register the grammar before the component renders, preferably once in a module rather than inside a React component.
Defer grammar loadingload-async-build
import { PrismAsyncLight as SyntaxHighlighter } from 'react-syntax-highlighter';
import { atomDark } from 'react-syntax-highlighter/dist/esm/styles/prism';
<SyntaxHighlighter language="typescript" style={atomDark} showLineNumbers>
{source}
</SyntaxHighlighter>The bundler must support dynamic import, and the code may render briefly without highlighting while chunks load.
Start line numbers at an offsetshow-line-numbers
<SyntaxHighlighter
language="python"
style={theme}
showLineNumbers
startingLineNumber={40}
>
{source}
</SyntaxHighlighter>startingLineNumber has visible effect only when showLineNumbers is enabled.
Mark changed linesstyle-specific-lines
const changed = new Set([3, 4]);
<SyntaxHighlighter
language="diff"
style={theme}
wrapLines
lineProps={(line) => ({
style: changed.has(line) ? { background: '#3b1f2b' } : undefined,
})}
>
{source}
</SyntaxHighlighter>lineProps requires wrapLines because each source line needs its own wrapper element.
Wrap long source lineswrap-long-lines
<SyntaxHighlighter
language="json"
style={theme}
wrapLongLines
customStyle={{ maxWidth: '100%', margin: 0 }}
>
{json}
</SyntaxHighlighter>wrapLongLines changes white-space to pre-wrap, which improves narrow layouts but no longer preserves horizontal code alignment.
Use external highlight.js CSSuse-css-classes
import 'highlight.js/styles/github-dark.css';
<SyntaxHighlighter language="shell" useInlineStyles={false}>
{command}
</SyntaxHighlighter>Disabling inline styles emits class names, but the component does not load the CSS file for you.
Replace the outer and code tagscustomize-tags
<SyntaxHighlighter
language="css"
style={theme}
PreTag="div"
CodeTag="span"
codeTagProps={{ className: 'source-code' }}
>
{css}
</SyntaxHighlighter>Changing semantic tags can hurt accessibility, so keep a code role or equivalent structure when the content is source code.
Pass attributes to the outer elementadd-pre-attributes
<SyntaxHighlighter
language="sql"
style={theme}
aria-label="SQL query"
data-testid="query-source"
customStyle={{ borderRadius: 8 }}
>
{sql}
</SyntaxHighlighter>Unknown component props are spread onto the outer pre element; codeTagProps targets the inner code element instead.
Check the bundled language listinspect-supported-languages
import SyntaxHighlighter from 'react-syntax-highlighter';
const canHighlight = SyntaxHighlighter.supportedLanguages.includes(language);
<SyntaxHighlighter language={canHighlight ? language : 'text'}>
{source}
</SyntaxHighlighter>Light builds contain only registered grammars, so application code should maintain its own allowed-language list there.
Alternatives
| Package | Registry | Pick it when |
|---|---|---|
| prism-react-renderer | npm | You want a smaller React-focused Prism renderer and are comfortable owning the code-block markup |
| react-code-blocks | npm | You want batteries-included code blocks with copy controls and themes more than low-level rendering control |
| lowlight | npm | You need a highlight.js syntax tree but can render it yourself or process it during a Markdown build |