Previously, my blog used a custom code renderer with sugar-high for syntax highlighting (from the Vercel template this site started from).
However, I ran into formatting and display issues and accessibility concerns, so I switched to Shiki using the official @shikijs/rehype plugin.
This tutorial will be easiest to follow if you started from the same Vercel portfolio starter kit template. (or a similiar one) as that I used.
How to use Shiki in Next.js (MDX)
1. Install the package
First, install shiki and the rehype plugin (and remove sugar-high if you still have it):
npm uninstall sugar-high
npm install shiki @shikijs/rehype
2. Update your MDX renderer
import { MDXRemote } from 'next-mdx-remote/rsc'
import rehypeShiki from '@shikijs/rehype'
export function CustomMDX(props) {
return (
<MDXRemote
{...props}
components={/* your custom components (if you have any) */}
options={{
mdxOptions: {
rehypePlugins: [[rehypeShiki, {
// pick a bundled theme; I use a darker, higher-contrast one
theme: 'material-theme-darker',
// optionally set languages and aliases
langs: ['tsx', 'typescript', 'bash', 'css', 'yaml', 'hcl'],
langAlias: { ts: 'typescript', js: 'javascript', sh: 'bash' },
}]],
},
}}
/>
)
}
3. Remove highlight.js CSS if present
If you previously used Highlight.js, remove any highlight.js/styles/*.css imports. Shiki inlines token colors, so you don't need a global theme stylesheet.
4. Remove old sugar-high styles from global.css (if any)
If you started from the same template, you might have --sh-* CSS variables. Remove those to avoid confusion with Shiki's output. For reference, this is what I removed initially:
:root {
--sh-class: #2d5e9d;
--sh-identifier: #354150;
--sh-sign: #8996a3;
--sh-string: #007f7a;
--sh-keyword: #e02518;
--sh-comment: #a19595;
--sh-jsxliterals: #6266d1;
--sh-property: #e25a1c;
--sh-entity: #e25a1c;
}
@media (prefers-color-scheme: dark) {
:root {
--sh-class: #4c97f8;
--sh-identifier: white;
--sh-keyword: #f47067;
--sh-string: #0fa295;
}
html {
color-scheme: dark;
}
}
.prose pre {
@apply bg-neutral-50 dark:bg-neutral-900 rounded-lg overflow-x-auto border border-neutral-200 dark:border-neutral-900 py-2 px-3 text-sm;
}
.prose code {
@apply px-1 py-0.5 rounded-lg;
}
.prose pre code {
@apply p-0;
border: initial;
line-height: 1.5;
}
.prose code span {
@apply font-medium;
}
.prose p {
@apply my-4 text-neutral-800 dark:text-neutral-200;
}
Results
Code blocks now render consistently on the server with better readability, and I can control themes and accessibility centrally.
Conclusion
Switching to Shiki solved the formatting and display issues I had with the default renderer and gives me more control over accessibility.