Add Syntax Highlighting to a Next.js Markdown Blog with rehype-prism

Tutorial
Post Image for Add Syntax Highlighting to a Next.js Markdown Blog with rehype-prism

Introduction

This post documents my learnings and the steps I took to implement syntax highlighting in a Next.js Markdown blog.

I was using Gatsby before; one benefit of using Gatsby was the rich ecosystem of plugins. To add syntax highlighting in Gatsby, you add plugin. For Next.js, you need to use several libraries, write more custom code, and understand the tradeoffs in the different approaches.

This made creating a blog in Next.js challenging, but the benefit was more flexibility.

Prerequisites

I highly recommend going through the Next.js tutorial to get a high overview of how Next.js works. Once you go through the tutorial, you will feel more comfortable taking the task of adding syntax highlighting.

Setup

On Github, Next.js has several example implementations. Make sure to download the blog-starter example.


Markdown To Html

Let's move to the markdownToHtml.js file. Here, transformation from Markdown to HTML is handle by remark-html and remark.

Coming from a Gatsby ecosystem, the remark name is a bit different from what remark in the unified ecosystem implies. As I learned from sw-yx's issue, there is no Prism plugin for remark, so we have to use @mapbox/rehype-prism.

import remark from 'remark'
import html from 'remark-html'

export default async function markdownToHtml(markdown) {
  const result = await remark().use(html).process(markdown)
  return result.toString()
}

Implementation

Let's remove remark-html and remark from the markdownToHtml.js file and install the following dependencies:

npm i unified
npm i remark-parse
npm i remark-rehype
npm i rehype-stringify
npm i @mapbox/rehype-prism

Once we have installed all the dependencies, let's add them to the markdownToHtml.js document. It should look something like this:

import unified from 'unified'
import remarkParse from 'remark-parse'
import remarkRehype from 'remark-rehype'
import rehypeStringify from 'rehype-stringify'
import mdxPrism from 'mdx-prism'

export default async function markdownToHtml(markdown) {
  const result = await unified()
    .use(remarkParse)
    .use(remarkRehype)
    .use(mdxPrism)
    .use(rehypeStringify)
    .process(markdown)
  return result.toString()
}

Recap

Let's walk through the code.

  1. unified is an interface for manipulating content. It does this by taking Markdown, HTML, or plain text, turning it into structured data, and making it available to other plugins. unified also lets you write one line of code to chain a feature into the process, exactly what we are doing in the markdownToHtml.js document.

  2. remark-parse processes the Markdown without compiling it to HTML.

  3. remark-rehype is then used to transform the processed Markdown to HTML.

  4. rehype-stringify is used to serialize the HTML.

  5. @mapbox/rehype-prism - is a rehype plugin to highlight code blocks in HTML with Prism. Although we are not parsing MDX, we can still parse our Markdown with this rehype plugin.

And now, if you start Next.js in development and visit one of the pages that have markdown, you will not see any changes. This is because you still needed to add the Prism styles to get the syntax highlighting.

Add Prism Theme

There are several ways to add a Prism theme. I've found the more straightforward implementation is adding it via a CDN link in your meta.js document. I'm adding my favorite theme, night-owl.

<link
  href="https://unpkg.com/prism-theme-night-owl@1.4.0/build/style.css"
  rel="stylesheet"
/>

Once you import Prism styles we're done! You now have syntax highlighting in your Next.js markdown blog! 🎉


Conclusion

Although you don't have plugins in Next.js, unlike Gatsby, you have several approaches to adding syntax highlighting through a wide array of libraries. It took me several hours of research to implement this feature. I hope that this post saves you some time!

If you want to add line highlighting capabilities check out @mapbox/rehype-prism. Props to Juan Olvera for expanding this capability. I took his fork and created my own, where I started working on adding line numbers. It is still progress. Hopefully, I'll get it up and running.

Please let me know if anything was unclear, or if there's anything else you'd like to see in this or a subsequent article.

© Lauro Silva, LLC. All rights reserved.