This Blog - Code Highlighting

May 15, 2024

Here is the link to the Dave Gray's youtube on how to build a markdown based blog in Next.js and Tailwind CSS. He uses Next 13 and the app router that was under development at the time. I did not have any issues following his tutorial using the latest version of Next.js (14.2.3).

As suggested at the end, I wanted to make some of my own changes. Some of my modifications include blog posts by sections and I updated the YAML to include an abstract on the post for the main page. One thing I noticed when creating a blog post that used code blocks in markdown is there was no highlighting. This posts shows how you can modify what was given in the original tutorial to have a highlighting theme for your markdown code blocks.

Here is a code block using Tailwind's Typography showing the getPostData function from the tutorial.

export async function getPostData(id: string) {
  const fullPath = path.join(postsDirectory, `${id}.md`);
  const fileContents = fs.readFileSync(fullPath, 'utf8');

  // Use gray-matter to parse the post metadata section
  const matterResult = matter(fileContents);

  const processedContent = await remark()
    .use(html)
    .process(matterResult.content);

  const contentHtml = processedContent.toString();

  const blogPostWithHTML: BlogPost & { contentHtml: string } = {
    id,
    title: matterResult.data.title,
    date: matterResult.data.date,
    contentHtml,
  };

  // Combine the data with the id
  return blogPostWithHTML;
}

Here is how we can change the function using markdownIt and highlightjs to improve the formatting of the code. We can replace remark() with markdownIt (npm install markdown-it) and then npm install markdown-it-highlightjs to add highlighting using css style sheets.

Define a constant md and tell it to use highlightjs. Note here, I have also modified the rendering of the markdown to allow for html. This allows me to include links using anchor tags in the posts and set the target to blank. Markdown would normally behave this way, but without setting this option markdownIt renders html as text.

import markdownIt from 'markdown-it';
import highlightjs from 'markdown-it-highlightjs';
const md = markdownIt({ html: true }).use(highlightjs);

export async function getPostData(id: string) {
  const fullPath = path.join(postsDirectory, `${id}.md`);
  const fileContents = fs.readFileSync(fullPath, 'utf8');

  // Use gray-matter to parse the post metadata section
  const matterResult = matter(fileContents);

  // Use markdown-it to render the content and highlight js to add css classes
  const result = md.render(matterResult.content);

  const contentHtml = result.toString();

  const blogPostWithHTML: BlogPost & { contentHtml: string } = {
    id,
    title: matterResult.data.title,
    date: matterResult.data.date,
    subject: matterResult.data.subject,
    abstract: matterResult.data.abstract,
    contentHtml,
  };

  // Combine the data with the id
  return blogPostWithHTML;
}

You can select a theme from here: HighlightJs themes
And once you found one, find the respective CSS styles on their GitHub repo. In your application, create a new CSS file called code.css, and in the globals.css import it like so:

@import 'code';

References:

There were several posts out there on how to do this, I found this post by Chris Bongers to be the most helpful and that is what I have followed here.

This post also decsribes the exact problem I wanted to solve. They suggests modifying the tailwind.config.ts, but I have not done that here. I saw sevreal others suggesting you turn off the default behavior of Typography but I found that was not needed here.

Notes:

In case you were wondering, I used a <div> tag here to wrap my code blocks so I could turn off the highlighting at the intro to this post. Someday, I would like to have a dark & light theme toggle and by creating two css classes for the different highlighting themes would be a way to do this.

For example, I wrapped the first markdown code block in <div class="default"> to show the code block how it looked without theming:

.hljs {
  /* atom-one-dark theme */
  color: #abb2bf;
  background: #282c34;
}

div.default .hljs {
  /*default all white */
  background: #1e293b;
  color: #e2e8f0;
}

← Back to home