Syntax Highlighting with Prismjs and Nextjs

When setting up this blog I found myself going in circles a bit trying to get code snippets and syntax highlighting to work. This blog is a Next.js blog with content served from a DatoCMS instance and the two together proved to complicate my life a little bit more than I would have liked. I did, however, finally manage to find an elegant solution that I think worked quite nicely so I decided to share it for anyone who might find themselves in the same situation.

This article assumes you already have a Nextjs site already up and running and are using Typescript with it. (if you aren't using Typescript) just name your files .js (instead of .tsx) and remove any Type references.

The example code below will give you the exact same implementation that I have on this blog to show code blocks it should work with any React based frontend, so don't feel like you're limited to just Nextjs.


Building the component

To get started, first install Prismjs.

In our example we used prism-react-renderer which has better built in handling than the main Prism package for React based applications and comes with a lot of defaults preset for you and doesn't polluate the global application.

So, to install prism-react-renderer, run the following command.

# npm
npm install --save prism-react-renderer
# yarn
yarn add prism-react-renderer

Now we'll setup a reusable component that contains all the logic and settings we need to display code consistently across our app.

Create a new folder (in your components dir) called code-block and copy the follwing code into a file called index.tsx.

1// src/components/code-block/index.tsx
2
3/* eslint-disable react/jsx-key */
4import Highlight, { defaultProps, Language } from "prism-react-renderer";
5import oceanicNext from "prism-react-renderer/themes/oceanicNext";
6
7import styles from "./index.module.css";
8
9interface Props {
10 code: string;
11 language: Language;
12 showLineNumbers: boolean;
13}
14
15const CodeBlock = ({ code, language, showLineNumbers }: Props) => {
16 return (
17 <Highlight
18 {...defaultProps}
19 code={code}
20 language={language}
21 theme={oceanicNext}
22 >
23 {({ className, style, tokens, getLineProps, getTokenProps }) => (
24 <pre className={className} style={style}>
25 {tokens.map((line, i) => (
26 <div {...getLineProps({ line, key: i })}>
27 {showLineNumbers && (
28 <span className={styles.lineNumber}>{i + 1}</span>
29 )}
30 <span className={styles.lineContent}>
31 {line.map((token, key) => (
32 <span {...getTokenProps({ token, key })} />
33 ))}
34 </span>
35 </div>
36 ))}
37 </pre>
38 )}
39 </Highlight>
40 );
41};
42
43export { CodeBlock} ;

You can change the theme by swapping out oceanicNext for any of the following supported themes.

The styles in this examples are implemented using PostCSS. So you'll also need to create a file index.module.css and paste the following styles into it so that the line numbers are styled correctly and don't break in the code block.

1/* src/components/code-block/index.module.css */
2
3.lineContent,
4.lineNumber {
5 display: table-cell;
6}
7.lineNumber {
8 text-align: right;
9 padding-right: 1.5rem;
10 user-select: none;
11 opacity: 0.4;
12 min-width: 2.5rem;
13}

Implementing the component

And finally, to implement this in an actual component or page, use the CodeBlock component like this:

1// src/components/{your-component}/index.tsx
2
3import { CodeBlock } from "../components/code-block";
4
5const exampleCode = `
6 (function codedemo() {
7 var hello = "Hello World!";
8 console.log(hello);
9 })();
10
11 return () => <App />;
12`;
13
14const language = 'javascript';
15
16const YourComponent = () => {
17 return (
18 <div>
19 <CodeBlock
20 code={exampleCode}
21 language={language}
22 showLineNumbers={exampleCode.split(/\n/).length > 10}
23 />
24 </div>
25 )
26}

You can see in the above usage example that I've just created a simple string of code that is passed into the Prism component. Ultimately this should come from CMS or even parsed Markdown and passed into the component programatically.

See my follow up article on how to use custom CodeBlocks with DatoCMS StructuredText block.

Final note

I've also configured the above to only show Line Numbers in the editor if the snippet of code is greater than 10 lines. This keeps things looking a bit nicer and less messy when you are displaying just a bit of code.

For more information on prism-react-renderer, check out their github.