Fix: Prism React Renderer Not Working — No Syntax Colors, Wrong Language, or Custom Theme Not Applying
Quick Answer
How to fix prism-react-renderer issues — Highlight component setup, language support, custom themes, line highlighting, copy button, and integration with MDX and documentation sites.
The Problem
The code block renders but has no colors:
import { Highlight, themes } from 'prism-react-renderer';
function CodeBlock({ code }: { code: string }) {
return (
<Highlight code={code} language="typescript" theme={themes.nightOwl}>
{({ tokens, getLineProps, getTokenProps }) => (
<pre>
{tokens.map((line, i) => (
<div key={i} {...getLineProps({ line })}>
{line.map((token, j) => (
<span key={j} {...getTokenProps({ token })} />
))}
</div>
))}
</pre>
)}
</Highlight>
);
}
// Renders plain text without syntax highlightingOr a language isn’t recognized:
Language "rust" is not supportedOr the v2 migration breaks existing code:
Module '"prism-react-renderer"' has no exported member 'Prism'Why This Happens
prism-react-renderer is a React component that uses PrismJS for syntax highlighting without dangerouslySetInnerHTML:
- Version 2 changed the API significantly — v1 used
Highlightas a default export withPrismpassed as a prop. v2 exportsHighlightas a named export and bundles Prism internally. Mixing v1 and v2 APIs causes import errors. - Limited languages are bundled by default — to keep the bundle small, prism-react-renderer only includes ~30 common languages. Rust, Go, Ruby, and many others need additional Prism languages loaded separately.
- The theme must be passed to
Highlight— without athemeprop, tokens are generated but have no color styles. The render prop gives you un-styled tokens that you must apply styles to. - The
stylefromgetLineProps/getTokenPropscontains the colors — if you override styles or don’t spread the props, colors are lost.
Fix 1: Basic Setup (v2)
npm install prism-react-renderer'use client';
import { Highlight, themes } from 'prism-react-renderer';
interface CodeBlockProps {
code: string;
language: string;
}
function CodeBlock({ code, language }: CodeBlockProps) {
return (
<Highlight
code={code.trim()}
language={language}
theme={themes.nightOwl} // Built-in themes
>
{({ className, style, tokens, getLineProps, getTokenProps }) => (
<pre
className={className}
style={{
...style,
padding: '16px',
borderRadius: '8px',
overflow: 'auto',
fontSize: '14px',
lineHeight: '1.6',
}}
>
{tokens.map((line, i) => (
<div key={i} {...getLineProps({ line })}>
{/* Optional: line numbers */}
<span style={{
display: 'inline-block',
width: '2em',
textAlign: 'right',
marginRight: '1em',
color: '#888',
userSelect: 'none',
}}>
{i + 1}
</span>
{line.map((token, j) => (
<span key={j} {...getTokenProps({ token })} />
))}
</div>
))}
</pre>
)}
</Highlight>
);
}
// Available built-in themes:
// themes.dracula, themes.duotoneDark, themes.duotoneLight,
// themes.github, themes.jettwaveDark, themes.jettwaveLight,
// themes.nightOwl, themes.nightOwlLight, themes.oceanicNext,
// themes.okaidia, themes.oneDark, themes.oneLight,
// themes.palenight, themes.shadesOfPurple, themes.synthwave84,
// themes.ultramin, themes.vsDark, themes.vsLightFix 2: Add Unsupported Languages
import { Highlight, Prism, themes } from 'prism-react-renderer';
// prism-react-renderer bundles these languages:
// markup, jsx, tsx, swift, kotlin, objectivec, js-extras,
// reason, rust, graphql, yaml, go, cpp, markdown, python,
// css, javascript, typescript, json, bash, sql
// For additional languages, extend Prism:
// Note: In v2, Prism is exported from prism-react-renderer
// Add Ruby support
(typeof global !== 'undefined' ? global : window).Prism = Prism;
await import('prismjs/components/prism-ruby');
// Add PHP support
await import('prismjs/components/prism-php');
// Add Rust (already included) — just verify:
console.log(Prism.languages.rust); // Should be defined
// Synchronous approach for build-time (SSR)
import 'prismjs/components/prism-ruby';
import 'prismjs/components/prism-php';
import 'prismjs/components/prism-java';
import 'prismjs/components/prism-csharp';
import 'prismjs/components/prism-dart';
import 'prismjs/components/prism-toml';
import 'prismjs/components/prism-docker';
function RubyCodeBlock({ code }: { code: string }) {
return (
<Highlight code={code} language="ruby" theme={themes.oneDark}>
{({ className, style, tokens, getLineProps, getTokenProps }) => (
<pre className={className} style={style}>
{tokens.map((line, i) => (
<div key={i} {...getLineProps({ line })}>
{line.map((token, j) => (
<span key={j} {...getTokenProps({ token })} />
))}
</div>
))}
</pre>
)}
</Highlight>
);
}Fix 3: Custom Theme
import { Highlight, type PrismTheme } from 'prism-react-renderer';
const myTheme: PrismTheme = {
plain: {
color: '#e0e0e0',
backgroundColor: '#1a1a2e',
},
styles: [
{
types: ['comment', 'prolog', 'doctype', 'cdata'],
style: { color: '#6a737d', fontStyle: 'italic' },
},
{
types: ['string', 'attr-value'],
style: { color: '#a5d6ff' },
},
{
types: ['number', 'boolean'],
style: { color: '#79c0ff' },
},
{
types: ['keyword', 'operator'],
style: { color: '#ff7b72' },
},
{
types: ['function'],
style: { color: '#d2a8ff' },
},
{
types: ['class-name', 'tag'],
style: { color: '#7ee787' },
},
{
types: ['attr-name'],
style: { color: '#79c0ff' },
},
{
types: ['punctuation'],
style: { color: '#8b949e' },
},
{
types: ['variable', 'constant', 'symbol'],
style: { color: '#ffa657' },
},
{
types: ['builtin', 'char', 'selector'],
style: { color: '#7ee787' },
},
],
};
<Highlight code={code} language="typescript" theme={myTheme}>
{/* ... */}
</Highlight>Fix 4: Line Highlighting and Copy Button
'use client';
import { Highlight, themes } from 'prism-react-renderer';
import { useState } from 'react';
interface CodeBlockProps {
code: string;
language: string;
filename?: string;
highlightLines?: number[]; // Lines to highlight (1-indexed)
}
function CodeBlock({ code, language, filename, highlightLines = [] }: CodeBlockProps) {
const [copied, setCopied] = useState(false);
async function handleCopy() {
await navigator.clipboard.writeText(code.trim());
setCopied(true);
setTimeout(() => setCopied(false), 2000);
}
return (
<div style={{ position: 'relative', borderRadius: '8px', overflow: 'hidden' }}>
{/* Header with filename and copy button */}
<div style={{
display: 'flex', justifyContent: 'space-between', alignItems: 'center',
padding: '8px 16px', background: '#1a1a2e', borderBottom: '1px solid #333',
color: '#888', fontSize: '12px',
}}>
<span>{filename || language}</span>
<button
onClick={handleCopy}
style={{ background: 'none', border: 'none', color: '#888', cursor: 'pointer' }}
>
{copied ? 'Copied!' : 'Copy'}
</button>
</div>
<Highlight code={code.trim()} language={language} theme={themes.nightOwl}>
{({ className, style, tokens, getLineProps, getTokenProps }) => (
<pre className={className} style={{ ...style, margin: 0, padding: '16px', overflow: 'auto' }}>
{tokens.map((line, i) => {
const lineProps = getLineProps({ line });
const isHighlighted = highlightLines.includes(i + 1);
return (
<div
key={i}
{...lineProps}
style={{
...lineProps.style,
backgroundColor: isHighlighted ? 'rgba(255, 255, 255, 0.1)' : undefined,
borderLeft: isHighlighted ? '3px solid #60a5fa' : '3px solid transparent',
paddingLeft: '12px',
marginLeft: '-12px',
marginRight: '-12px',
paddingRight: '12px',
}}
>
{line.map((token, j) => (
<span key={j} {...getTokenProps({ token })} />
))}
</div>
);
})}
</pre>
)}
</Highlight>
</div>
);
}
// Usage
<CodeBlock
code={`function greet(name: string) {
console.log(\`Hello, \${name}!\`);
return name;
}`}
language="typescript"
filename="greeting.ts"
highlightLines={[2]} // Highlight line 2
/>Fix 5: MDX Integration
// components/MDXComponents.tsx — use as MDX code block renderer
import { Highlight, themes } from 'prism-react-renderer';
function MDXCodeBlock({ children, className }: {
children: string;
className?: string;
}) {
// Extract language from className (e.g., "language-typescript")
const language = className?.replace('language-', '') ?? 'text';
// Inline code (no className) — render as <code>
if (!className) {
return (
<code style={{
background: '#f0f0f0',
padding: '2px 6px',
borderRadius: '4px',
fontSize: '0.9em',
}}>
{children}
</code>
);
}
// Fenced code block — use Prism
return (
<Highlight code={children.trim()} language={language} theme={themes.nightOwl}>
{({ className: hlClassName, style, tokens, getLineProps, getTokenProps }) => (
<pre className={hlClassName} style={{ ...style, padding: '16px', borderRadius: '8px', overflow: 'auto' }}>
{tokens.map((line, i) => (
<div key={i} {...getLineProps({ line })}>
{line.map((token, j) => (
<span key={j} {...getTokenProps({ token })} />
))}
</div>
))}
</pre>
)}
</Highlight>
);
}
// Register as MDX component
export const mdxComponents = {
code: MDXCodeBlock,
pre: ({ children }: any) => <>{children}</>, // Unwrap <pre> — Highlight adds its own
};Fix 6: Dual Theme (Light/Dark Mode)
'use client';
import { Highlight, themes } from 'prism-react-renderer';
import { useTheme } from 'next-themes';
function ThemedCodeBlock({ code, language }: { code: string; language: string }) {
const { resolvedTheme } = useTheme();
const theme = resolvedTheme === 'dark' ? themes.nightOwl : themes.nightOwlLight;
return (
<Highlight code={code.trim()} language={language} theme={theme}>
{({ className, style, tokens, getLineProps, getTokenProps }) => (
<pre className={className} style={{ ...style, padding: '16px', borderRadius: '8px' }}>
{tokens.map((line, i) => (
<div key={i} {...getLineProps({ line })}>
{line.map((token, j) => (
<span key={j} {...getTokenProps({ token })} />
))}
</div>
))}
</pre>
)}
</Highlight>
);
}Still Not Working?
Code renders as plain text (no colors) — verify you’re spreading {...getTokenProps({ token })} on each <span>. The token props include inline style with the color from the theme. If you override style without spreading the original, colors are lost.
v1 code doesn’t work after upgrading to v2 — v2 changed the API. import Highlight, { defaultProps, Prism } (v1) is now import { Highlight, themes, Prism } (v2). The {...defaultProps} spread on <Highlight> is no longer needed. Remove it and pass theme directly.
Language not recognized — prism-react-renderer includes ~30 languages. For others, install prismjs and import the language component: import 'prismjs/components/prism-ruby'. Set globalThis.Prism = Prism before importing.
Hydration mismatch in Next.js — if the theme depends on client state (dark mode), the server render may not match the client. Use suppressHydrationWarning on the <pre> element, or render a placeholder during SSR and the actual code block after hydration.
For related code highlighting issues, see Fix: Shiki Not Working and Fix: MDX Not Working.
Solo developer based in Japan. Every solution is cross-referenced with official documentation and tested before publishing.
Was this article helpful?
Related Articles
Fix: AutoAnimate Not Working — Transitions Not Playing, List Items Not Animating, or React State Changes Ignored
How to fix @formkit/auto-animate issues — parent ref setup, React useAutoAnimate hook, Vue directive, animation customization, disabling for specific elements, and framework integration.
Fix: Blurhash Not Working — Placeholder Not Rendering, Encoding Failing, or Colors Wrong
How to fix Blurhash image placeholder issues — encoding with Sharp, decoding in React, canvas rendering, Next.js image placeholders, CSS blur fallback, and performance optimization.
Fix: Embla Carousel Not Working — Slides Not Scrolling, Autoplay Not Starting, or Thumbnails Not Syncing
How to fix Embla Carousel issues — React setup, slide sizing, autoplay and navigation plugins, loop mode, thumbnail carousels, responsive breakpoints, and vertical scrolling.
Fix: i18next Not Working — Translations Missing, Language Not Switching, or Namespace Errors
How to fix i18next issues — react-i18next setup, translation file loading, namespace configuration, language detection, interpolation, pluralization, and Next.js integration.