Published on Saturday 11 May 2024
Syntax Highlighting for Markdown in Next.js 13 (The Right Way)
Enhance your Next.js 13 project with this detailed guide on implementing PrismJS for dynamic syntax highlighting in Markdown-rendered content.
In this tutorial, we'll explore how to add syntax highlighting to Markdown-rendered content in a Next.js 13 application. Whether you're building a blog, documentation, or any site that features code snippets, syntax highlighting is essential for readability. We'll use PrismJS for syntax highlighting and markdown-to-jsx to convert Markdown into JSX.
Syntax highlighting is a feature used in text editors and web pages that displays source code in different colors and fonts according to the category of terms. This not only enhances the aesthetic appeal but also increases the readability and understanding of the code. PrismJS, our choice for this tutorial, is a popular syntax highlighting library because it's lightweight, easy to use, and supports a wide range of languages. By integrating PrismJS into your Next.js project, you can significantly improve how code snippets are visualized in your Markdown content.
Prerequisites
Before we begin, ensure you have the following:
- Node.js installed on your system.
- A basic understanding of Next.js and React.
- An existing Next.js 13 project. If you don't have one, create one using
npx create-next-app
. - I assume you are using
markdown-to-jsx
to convert Markdown into JSX components. This package allows us to integrate Markdown content seamlessly within our React components. If not, I recommend reading its npm page.
Step 1: Install Required Packages
First, install markdown-to-jsx
and prismjs
:
npm install prismjs markdown-to-jsx @types/prismjs @types/markdown-to-jsx
This command will not only install markdown-to-jsx
and prismjs
, but also their TypeScript definitions (@types/prismjs
and @types/markdown-to-jsx
).
If you want to know why I like to use Typescript, read this.
Step 2: Create the CodeBlock Component
The CodeBlock component will use PrismJS to apply syntax highlighting. Create CodeBlock.tsx:
// CodeBlock.tsx
import React from 'react';
import Prism from 'prismjs';
import 'prismjs/components/prism-typescript'; // Ensure TypeScript support
interface CodeBlockProps {
language?: string;
children: React.ReactNode; // More accurate typing for React children
}
const CodeBlock: React.FC<CodeBlockProps> = ({ language = 'typescript', children }) => {
// Extract string from ReactNode if it's a code element
const codeString = typeof children === 'string' ? children : React.Children.toArray(children).reduce<string>((acc, child) => {
if (React.isValidElement(child) && typeof child.props.children === 'string') {
return acc + child.props.children;
}
return acc;
}, '');
// Apply syntax highlighting
const html = Prism.highlight(codeString, Prism.languages[language] || Prism.languages.plaintext, language);
return (
<pre className={`language-${language}`}>
<code className={`language-${language}`} dangerouslySetInnerHTML={{ __html: html }} />
</pre>
);
};
export default CodeBlock;
It uses Prism.highlight
, a function provided by PrismJS, to transform plain text code into formatted HTML by applying CSS styles based on the language specified. The component dynamically extracts the code string from its children, applies highlighting, and safely injects the resulting HTML using React's dangerouslySetInnerHTML
to preserve the integrity of the syntax styling.
Step 3: Include PrismJS Theme in Your Application
You need to pass these options
interface OverrideProps {
children?: React.ReactNode;
className?: string;
}
const markdownOptions = {
overrides: {
pre: { // Assuming all code blocks are wrapped in `pre` tags in Markdown
component: CodeBlock,
props: ({ children, className = '' }: OverrideProps) => {
// Extract the language from the className
const match = /language-(\w+)/.exec(className);
const language = match ? match[1] : 'plaintext'; // Default to 'plaintext' if no language is detected
return { language, children };
}
}
},
};
To the Markdown element, so I had to turn this
//...
<Markdown>{post.content}</Markdown>
//...
into this
//...
<Markdown options={markdownOptions}>{post.content}</Markdown>
//...
Step 4: Include PrismJS Theme in Your Application
Ensure that the PrismJS theme CSS is globally available across your application by importing it. In my case, I've included it in the 'page.css' file, which is located at the same level as 'page.tsx'. This is because I'm not utilizing the 'pages' directory.
@import 'prismjs/themes/prism-tomorrow.css';
Pick up any other one among default styles:
@import 'prismjs/themes/prism-dark.css';
@import 'prismjs/themes/prism-funky.css';
@import 'prismjs/themes/prism.css';
@import 'prismjs/themes/prism-okaidia.css';
Or manually download the CSS from this GitHub Repository and make it available in node_modules\prismjs\themes
.
Conclusion
You now have a fully functional setup for rendering Markdown with syntax highlighting in your Next.js 13 application. This setup enhances the readability of code snippets within your Markdown content, making them clearer and more engaging for your readers.