Next.js Blog (6): MDX Plugins (Shiki)
Contents
Notice
This text was originally translated using AI tools.
Introduction#
Previously, by introducing MDX, we secured extensibility through components.
During the application process,
I discovered that MDX allows you to decorate the body of your Markdown more diversely by utilizing various plugins.
In this post, focusing on the rehype library for code highlighting, I will summarize the process of installing and applying plugins.
The Absence of Code Block Highlighting#
With only the default Markdown settings, there were several disappointing aspects of the blog, particularly the code blocks, which are the most important part of a development blog.
- Monotonous Rendering: Code was output wrapped simply in
<pre>and<code>tags, making visual distinction difficult. - Limitations of Manual Setup: While background colors or fonts can be changed with CSS, it was nearly impossible to manually implement 'highlighting' that applies different colors for each language.
I was able to solve these problems through MDX plugins.
The Two Pillars of MDX Plugins: Remark and Rehype#
MDX supports two major types of plugins depending on the processing stage:
- Remark: Handles Markdown syntax (Markdown AST). (e.g., adding tables, checkboxes, footnotes, etc.)
- Rehype: Handles the converted HTML structure (HTML AST). (e.g., code highlighting, injecting classes, assigning IDs, etc.)
These plugins can be injected via the mdxOptions of the serialize function, which parses Markdown in Next.js.
// /utils/markdown-utils.ts
...
const mdxSource = await serialize(fileContents, {
parseFrontmatter: true,
mdxOptions: {
format: 'mdx',
remarkPlugins: [
// Plugins for Markdown syntax transformation
],
rehypePlugins: [
// Plugins for processing HTML output
],
},
});Chosen Highlighting Plugins#
Among many plugins, I chose the combination of rehype-pretty-code & shiki.
rehype-pretty-code
A plugin that maximizes the readability of code blocks
Beyond simply applying colors, it provides rich features such as displaying line numbers, highlighting specific lines, and highlighting words.shiki
The engine that actually analyzes the code internally
It uses the same highlighting engine as VS Code, making it highly accurate and providing various connected style themes.
📌 Were there no other plugins?
Initially, I first applied the combination of
rehype-highlightandhighlight.js.
While functionally sufficient for simple syntax coloring, I encountered the following inconveniences during operation:
- Separate management of CSS files was required when switching between light/dark modes.
- Certain areas remained unstyled because it only recognized pre-defined patterns.
Installing and Applying Plugins#
First, let's install the necessary packages.
You must install rehype-pretty-code along with the highlighting engine, shiki.
pnpm install rehype-pretty-code shikiOnce installed, add the plugin to the mdxOptions we looked at earlier.
You can configure it to support both light and dark mode themes simultaneously, rather than just a single theme.
// /utils/markdown-utils.ts
...
const mdxSource = await serialize(fileContents, {
parseFrontmatter: true,
mdxOptions: {
...
rehypePlugins: [
[rehypePrettyCode, {
// theme: 'github-dark',
theme: {
light: 'github-light',
dark: 'github-dark',
},
}],
],
},
});
In the example above, I used the standard github-light and github-dark. You can preview other high-quality themes by visiting the official Shiki documentation.
After that, simply writing the theme name of your choice in the theme property should apply it immediately...
or so I thought, but of course, it didn't work all at once. 😄
Style Conflict: Shiki vs. GitHub Markdown#
I was using the github-markdown-css package to establish the overall guidelines for the body area,
and an unexpected problem occurred here.
Because this package already strongly styles code blocks (<pre>, <code>),
a style conflict occurred with the Shiki-themed code blocks.
Here are the methods I considered to solve this:
-
Overwriting using CSS specificity (Selected)
Although it requires using!importantor specific selectors, I thought it was advantageous for maintenance because I wouldn't have to touch the external library. -
Disabling
github-markdown-css
This would be the cleanest, but it would change the overall style of the Markdown, requiring additional work.
Considering the ease of maintenance,
I chose the first method and decided to manage it by creating a separate shiki.css file.
/* shiki.css */
/* 1. Control light/dark mode variables */
:root {
--shiki-dark: inherit;
}
@media (prefers-color-scheme: dark) {
:root {
--shiki-light: inherit;
}
}
/* 2. Shiki element styles (Overwriting existing library styles) */
pre {
/* Use !important to ignore the background color from github-markdown-css */
background-color: var(--shiki-light-bg) !important;
font-family: var(--font-fira-code), 'Roboto Mono', monospace;
padding: 1.5rem;
border-radius: 8px;
overflow-x: auto;
}
pre code span {
color: var(--shiki-light);
}
/* 3. Handling Dark Mode */
@media (prefers-color-scheme: dark) {
pre {
background-color: var(--shiki-dark-bg) !important;
}
pre code span {
color: var(--shiki-dark);
}
}Conclusion#
After resolving the style conflict and rebuilding, I was finally able to confirm that the highlighting was applied correctly.
I solved the problem by forcibly overwriting the existing library's styles using CSS specificity. After finishing the implementation, I wondered if there might have been a "better way" to maintain cleaner control while reducing library dependency.
There are many other great plugins besides the ones introduced in this post, so check them out and use them as needed.