
- Why highlight.js with rehype-highlight?
- Step 1: Install Dependencies
- Step 2: Configure Next.js MDX
- Step 3: Create Custom CSS Styles
- Step 4: Import CSS in MDX Layout
- Step 5: Using Code Blocks in MDX
- Step 6: Language Detection
- Customization Options
- Using Different Themes
- Custom Language Support
- Troubleshooting
- Code Blocks Not Highlighting
- Styling Conflicts
- Performance Considerations
- Best Practices
- Conclusion
How to add syntax highlighting to MDX files in Next.js
Syntax highlighting is essential for any technical blog or documentation site. It makes code blocks more readable and visually appealing, helping readers understand the structure and syntax of your code examples. In this guide, we'll walk through how to integrate highlight.js into your Next.js MDX setup using rehype-highlight, with proper dark/light theme support and Tailwind CSS compatibility.
Why highlight.js with rehype-highlight?
When working with MDX in Next.js, you need a solution that processes your markdown at build time and applies syntax highlighting. The rehype-highlight
plugin is the perfect choice because:
- Build-time processing: Syntax highlighting is applied during the build process, not at runtime
- Lightweight: Only the necessary highlight.js styles are included
- Flexible: Supports custom themes and modifications
- MDX compatible: Works seamlessly with Next.js MDX setup
Step 1: Install Dependencies
First, install the required packages:
npm install rehype-highlight
The rehype-highlight
package will automatically install highlight.js
as a dependency.
Step 2: Configure Next.js MDX
Update your next.config.ts
to include the rehype-highlight plugin:
import type { NextConfig } from "next";
import createMDX from '@next/mdx'
import rehypeHighlight from 'rehype-highlight';
const nextConfig: NextConfig = {
output: 'export',
trailingSlash: true,
pageExtensions: ['js', 'jsx', 'md', 'mdx', 'ts', 'tsx'],
images: {
unoptimized: true,
}
};
const withMDX = createMDX({
extension: /\.(md|mdx)$/,
options: {
remarkPlugins: [],
rehypePlugins: [rehypeHighlight],
}
})
export default withMDX(nextConfig)
This configuration tells Next.js to process all .md
and .mdx
files with the rehype-highlight plugin, which will automatically detect code blocks and apply syntax highlighting.
Step 3: Create Custom CSS Styles
You'll need to copy the highlight.js CSS themes from the official repository and make some modifications for optimal integration with your Next.js setup.
Create a custom CSS file and copy your preferred theme (like github.css
for light mode and github-dark.css
for dark mode). Here are the key modifications you'll need to make:
Important modifications:
- Remove background colors: If you're using Tailwind Typography, remove the
background-color
from the.hljs
class to avoid conflicts with the prose styling - Add dark mode support: Wrap the dark theme styles in a
.dark
class selector to enable automatic theme switching - Preserve syntax colors: Keep all the syntax highlighting colors for both light and dark themes
Example of your highlight-js.css
file:
/* Copy light theme styles from highlight.js here*/
.hljs {
color: #24292e;
/* Remove background-color if using Tailwind Typography */
}
/* ... other light theme styles ... */
/* Dark theme styles wrapped in .dark class */
.dark {
/* Copy dark theme styles from highlight.js here*/
.hljs {
color: #c9d1d9;
}
/* ... other dark theme styles ... */
}
Step 4: Import CSS in MDX Layout
Create or update your MDX layout component to import the highlight.js styles:
import React from "react";
import "@/app/styles/highlight-js.css"
export default function MdxLayout({ children }: { children: React.ReactNode }) {
return (
<div>
{children}
</div>
)
}
This ensures that the highlight.js styles are loaded for all MDX content.
Step 5: Using Code Blocks in MDX
Now you can use code blocks in your MDX files with automatic syntax highlighting:
# Example MDX Content
Here's a JavaScript example:
```javascript
function greet(name) {
return `Hello, ${name}!`;
}
console.log(greet('World'));
```
And a TypeScript example:
```typescript
interface User {
id: number;
name: string;
email: string;
}
const user: User = {
id: 1,
name: 'John Doe',
email: 'john@example.com'
};
```
Python code works too:
```python
def fibonacci(n):
if n <= 1:
return n
return fibonacci(n-1) + fibonacci(n-2)
print(fibonacci(10))
```
The rehype-highlight plugin will automatically detect the language and apply the appropriate syntax highlighting.
Step 6: Language Detection
rehype-highlight automatically detects the language from the code fence. Supported languages include:
- JavaScript/TypeScript:
js
,javascript
,ts
,typescript
- Python:
python
,py
- Java:
java
- CSS:
css
- HTML:
html
- JSON:
json
- Bash/Shell:
bash
,sh
,shell
- SQL:
sql
- Markdown:
markdown
,md
- And many more...
Customization Options
Using Different Themes
You can easily switch to different highlight.js themes by replacing the CSS content. Popular alternatives include:
- Monokai: Dark theme with bright colors
- Dracula: Dark theme with purple accents
- Solarized Light/Dark: Easy on the eyes
- VS Code: Matches VS Code's default theme
Custom Language Support
By default, rehype-highlight supports only (37 languages)[https://github.com/wooorm/lowlight?tab=readme-ov-file#common] defined in common
object of lowlight library.
If you want to support all languages or a customized subset of languages, you can configure rehype-highlight:
import rehypeHighlight from 'rehype-highlight';
import elixir from 'highlight.js/lib/languages/elixir';
import rust from 'highlight.js/lib/languages/rust';
import { all } from 'lowlight';
const withMDX = createMDX({
extension: /\.(md|mdx)$/,
options: {
remarkPlugins: [],
rehypePlugins: [
[rehypeHighlight, { languages: all }] // supports all languages
//[rehypeHighlight, { languages: {elixir, rust} }] // supports only elixir and rust languages
],
}
})
Troubleshooting
Code Blocks Not Highlighting
- Check language specification: Make sure you specify the language in the code fence
- Verify plugin installation: Ensure
rehype-highlight
is properly installed - Check CSS import: Confirm the highlight.js CSS is imported in your MDX layout
Styling Conflicts
- Tailwind Typography conflicts: The custom CSS removes background colors to prevent conflicts
- Dark mode issues: Ensure your dark mode implementation uses the
.dark
class - Font conflicts: The CSS preserves font styling while adding syntax colors
Performance Considerations
- Build time: Syntax highlighting happens at build time, so it doesn't affect runtime performance
- Bundle size: Only the necessary highlight.js styles are included
- Caching: Highlighted code blocks are cached with your static export
Best Practices
- Always specify language: Even if auto-detection works, explicitly specifying the language ensures consistent highlighting
- Use semantic language names: Use
typescript
instead ofts
for better clarity - Test both themes: Ensure your code looks good in both light and dark modes
- Keep CSS minimal: Only include the styles you need to avoid conflicts
Conclusion
Integrating highlight.js with MDX in Next.js provides a powerful, performant solution for syntax highlighting. The combination of rehype-highlight for processing and custom CSS for styling gives you full control over the appearance while maintaining compatibility with Tailwind CSS and dark mode support.
The setup we've covered provides:
- Automatic syntax highlighting for code blocks
- Dark/light theme support
- Tailwind CSS compatibility
- Build-time processing for optimal performance
- Support for 190+ programming languages
- Customizable themes and styling
This approach is a good fit for technical blogs, documentation sites, and any project that needs to display code with proper syntax highlighting.
- Why highlight.js with rehype-highlight?
- Step 1: Install Dependencies
- Step 2: Configure Next.js MDX
- Step 3: Create Custom CSS Styles
- Step 4: Import CSS in MDX Layout
- Step 5: Using Code Blocks in MDX
- Step 6: Language Detection
- Customization Options
- Using Different Themes
- Custom Language Support
- Troubleshooting
- Code Blocks Not Highlighting
- Styling Conflicts
- Performance Considerations
- Best Practices
- Conclusion