Using Prism.js in a Next.js site

Prism.js is a syntax highlighter used by the Frendly blog to show styled code boxes, like this one...

    <h1>👋 Hello</h2>
    <h2>I'm some styled syntax!</h2>

If you're not using Next.js, the basic installation is to download your desired theme and languages and include the Prism.js at the end of the <body> and the Prism CSS in the HTML <head> (or use the CDN hosted files).

    <link href="/styles/Prism.css" rel="stylesheet" />
    // ...
    <script src="/js/Prism.js"></script>

To have Prism style blocks of code enclose the code in <pre> and <code> tags, with a CSS class on the <code> tag for the language you want to style. Here's the list of available languages.

    <code class="language-html">
      <div>The code I want to style</div>

The Prism JavaScript will pick up that structure/class name and convert what's in your <code> tags into <span> elements that it (or you!) applies styles to.

To try this basic installation in Next.js (which didn't work), I added the CSS to the <Head> in my <Layout> component and the Prism.js to a <Footer> component, just before the closing </body>.

This <Layout> component wraps the other page components so the <Head> and <Footer> will be on every page. You could of course have a different layout just for those pages that require the Prism functionality to save you importing it where it's not in use.

But wait, this didn't work! 😱 The styles would only show after a page refresh. This is because the Prism script is running before the DOM is available.

This calls for the React useEffect() hook, at which point the markup is available to apply the Prism JavaScript.

Instead of doing the above, install the Prismjs node module in your project. (In the root of your Next.js project folder open the terminal and type...)

Now the Prism files are available to your Next.js components.

Where you want to apply Prism import it from node modules and use the React useEffect() hook to call the Prism function highlightAll().

We just want highlightAll() to run once when the component DOM is ready, so we pass an empty array [] as the second parameter to the useEffect() hook, which is the same as the componentDidMount() method in React class components.

Next we need the CSS so import that in _app.js where global CSS is imported.

(I preferred to take these styles and overwrite some of them for the Frendly blog, like the background and font-size, so I've copied them to /styles/prism.css and link to that stylesheet from the next/head, as above).

If you look in the node_modules/prismjs/themes folder you'll see the themes available for import.

Now where you've used the <pre> and <code> tags with the correct language CSS class, if you load the page the styles should be applied.

Using Prism plugins and JSX styles

I'm styling JSX in some of my code blocks and for this I had to explicitly import the prism-jsx JavaScript so it would style as per other languages.

I was also having a problem with the code in the <code> block rendering as HTML. For this you can escape the HTML tags (&lt;div&gt;) but this is tedious.

I used the Prism Unescaped Markup plugin to handle this and simply imported it in my Layout component, and wrapped the code in comments, as per the plugin instructions.

One slight annoyance with this method, for every new line my code editor (VS Code) would remove the tab indent on any empty line, which Prism would output as a <p> tag, messing up my code box!

To fix this I've turned off the trimAutoWhitespace in VS Code settings. On Mac, go to Code > Preferences > Settings (or type cmd + ,), search "trimAutoWhitespace" and uncheck the checkbox. Your indents should remain in place on empty lines.

If you have any questions or comments or want to connect, you can follow me on Twitter, or sign up to the newsletter in the footer for front-end articles to your inbox 👇

Back home