If you are a developer and wants to share the things that you learn, then writing blog posts with code examples is a great idea – as I’m doing right now.

Or if you are developing some software, then also you need to write clear documentation and articles explaining how your product works with code samples for developers.

In either case, highlighting the syntax is essential. It makes the code on your website look like how it appears on modern code editors – better readability, understandability, and aesthetically pleasing.

This article shows you how to achieve that with Next.js.

There are many JavaScript syntax highlighters out there. The most popular one is – Highlight.js. It has over 22k GitHub stars as I am writing this post.

There are also other choices like Prism.js and Rainbow, but Highlight.js is the one I liked.

Earlier I had tried Prism.js as well. However, I found it difficult to make it work with Next.js. I faced some issues while importing the library to Next.js component.

With that being said, if you were using plain HTML or a normal templating language to write the content, then using these highlighter plugins is a piece of cake – just load the javascript file in your HTML within <script> tag, a theme CSS file, and call the appropriate function, like highlighAll().

How Highlighting Works in Next.js

But with Next.js, or React, the story is a bit different. Because directly manipulating the HTML Dom is not the right way. So loading the JavaScript file in <script> is not a solution.

Let’s start by assuming that you have some code content (like an HTML string) within JSX component. This will be usually within <pre></pre> tags.

Now in order to highlight that, the highlighter needs to parse the HTML within pre tags and convert the HTML with additional tags and CSS classes. These are then used to color highlight parts of the syntax based on the theme CSS file applied.

There are two approaches to doing this:

  1. Server-side conversion
  2. Client-side conversion and highlighting

In the second approach, the parsing, conversion, and highlighting is all done on the client browser. That’s what we’re going to discuss.

The advantage is that the unmodified HTML is sent to the client. So the document size is not blown up.

  • The highlight function is called within the useEffect() hook. This ensures that the necessary JavaScript code is also sent to the browser by Next.js.
  • There in the browser, the HTML is hydrated by Next.js and all actions given within the useEffect() hook are performed.
  • Note: Since useEffect() is a client-side hook, you should use it within a React Client component. It won’t work within a Server Component

Server-side highlighting too has some advantages. For instance, the JavaScript is executed on the server itself, and the modified HTML is sent to the client’s browser, thereby reducing the bundle size sent to the client.

However it can bloat the HTML document if you are highlighting large chunks of code. So I think you should do it on the server-side when the code block is small.

How to Use Highlight.js with Next.js

The first step is installing the package from your chosen repo:

npm install highlight.js

or,

yarn add highlight.js

Once it is installed, go to the component where you want to highlight code and set it as a ‘client component’.

Also import the useEffect hook.

'use client';
import { useEffect } from "react";

Following that, import the package along with all the languages you want to support:

import hljs from "highlight.js/lib/core";

import javascript from 'highlight.js/lib/languages/javascript';
import php from 'highlight.js/lib/languages/php';
import xml from 'highlight.js/lib/languages/xml';
import css from 'highlight.js/lib/languages/css';
import bash from 'highlight.js/lib/languages/bash';
import shell from 'highlight.js/lib/languages/shell';

_Note: HTML is supported by default, so no need to import that. Also, you can check the list of supported languages by checking the node_modules/highlight.js/lib/languages folder_

Then import a theme CSS file as well:

import 'highlight.js/styles/github-dark.css'

Then within the component function, call the highlightAll() method within useEffect:

export default function PostContent(props) {

    useEffect(() => {
        hljs.highlightAll();      
    }, [])

    return (
        <div dangerouslySetInnerHTML={{ __html: props.content }} />
    );
}

Here we’re assuming that props.content is a properly sanitized HTML string coming from a backend API or CMS.

Otherwise if it’s converted from Markdown, then also the same is applicable.

The highlightAll() method will look for all code blocks inside props.content and highlight them.

So make sure to properly place the code within <pre><code> tags and also add the lang-* attributes.

On the other hand, if you have very small code blocks to highlight, you can use the highlight() method instead.

export default function PostContent() {
    const code = `function findSum(a, b) {
                    return a + b;
                }

                const num1 = 5;
                const num2 = 10;

                const result = findSum(num1, num2);

                console.log(\`The sum of \${num1} and \${num2} is \${result}\`); `;

    const highlightedCode = hljs.highlight(code, { language: 'javascript'}).value;

    return (
        <div dangerouslySetInnerHTML={{ __html: highlightedCode }} />
    );
}

With highlight(), you need to explicitly mention the language. So it won’t work for markup that contain multiple code blocks.

In addition to these two functions, highlight.js supports methods like highlightAuto() and highlightElement(). The latter is useful if you want to highlight a DOM node.