Syntax Highlighting with Shiki, React Server Components, and Next.js

February 8, 2024 · 3 min read

2025 Update
The article was updated to make a singleton highlighter, as mentioned in the latest Shiki update, to resolve certain memory leaks when the highlighter is called from React Server Components. Here’s a list of the updates:
Updated
getHighlighterto be a singleton withmakeSingletonHighlighterRemoved custom Blade language as now it’s bundled with Shiki!
Using Shiki for syntax highlighting
Wouldn’t it be great if we could provide syntax highlighting to our readers without adding any extra JavaScript weight? That’s exactly what we’re going to do using Shiki, React Server Components (RSC), and Next.js.
Installing Shiki
First, we need to install Shiki:
npm install -D shikiWe create a React Server Component. In Next.js’s App Router, Server Components are the default:
export default async function Code({ code, language }) {
return <div></div>;
}We get a code and a language from the props in our component. The code prop might come from a CMS and usually has the HTML code block for our code examples. The language prop shows which programming language the syntax highlighter should use.
Creating a Helper Function
To keep our code clean, we will wrap the syntax highlighting logic in a helper function. Go ahead and make a file called shiki.js inside the utils folder, and add the following content:
import { createHighlighter, makeSingletonHighlighter } from 'shiki';
import { bundledLanguages } from 'shiki/bundle/web';
import antlers from '../../content/languages/antlers.json';
const getHighlighter = makeSingletonHighlighter(createHighlighter);
export const codeToHtml = async ({ code, language }) => {
const highlighter = await getHighlighter({
themes: ['github-light', 'github-dark'],
langs: [
...Object.keys(bundledLanguages),
{
id: 'antlers',
scopeName: 'text.html.statamic',
embeddedLangs: ['html'],
...antlers,
},
],
});
return highlighter.codeToHtml(code, {
lang: language,
themes: {
dark: 'github-dark',
light: 'github-light',
},
});
};Let’s break this down:
We import the web bundle from Shiki to minimize the load. If you need support for additional languages, you can import the full bundle without worrying about client-side load since this occurs server-side.
We use the
getHighlighterfunction from Shiki to configure our highlighter with additional features before performing the syntax highlighting.We’ve set up two themes for light and dark modes. If your site doesn’t flip between modes, you can just stick with one theme.
Shiki has built-in support for a lot of themes. Feel free to check the list.
const highlighter = await getHighlighter({
theme: 'github-dark',
});Custom languages with Shiki
We also include support for custom languages. For example, we’ve added Antlers for our Statamic ( Hey Statamic friends 👋 ) audience. We sourced Antlers from this repository.
Syntax highlighting
At the bottom of our JavaScript helper, we export the highlighter and the codeToHtml function, which accepts our language and themes and returns syntax-highlighted HTML.
return highlighter.codeToHtml(code, {
lang: language,
themes: {
dark: 'github-dark',
light: 'github-light',
},
});React Server component for syntax highlighting
With our helper function ready, let’s integrate it into our React Server Component:
import { codeToHtml } from '@/utils/shiki';
export default async function Code({ code, language }) {
const html = await codeToHtml({
code,
language,
});
return <div className="px-5" dangerouslySetInnerHTML={{ __html: html }} />;
}CSS for dual theme support
With our component and syntax highlighting set up, we can improve the user experience by adding CSS styles that match the user’s theme preferences. This step is optional but recommended if your site supports light and dark modes.
To implement theme-based rendering, you’ll need to have TailwindCSS configured with darkMode set to class.
Here’s an example of how you can define your CSS to switch between themes:
html.dark .shiki,
html.dark .shiki span {
color: var(--shiki-dark) !important;
background-color: var(--shiki-dark-bg) !important;
/* Optional, if you also want font styles */
font-style: var(--shiki-dark-font-style) !important;
font-weight: var(--shiki-dark-font-weight) !important;
text-decoration: var(--shiki-dark-text-decoration) !important;
}
/* Optional, for the code block we add overflow, borders and padding */
.shiki {
@apply overflow-x-auto rounded-xl p-5;
}By adding these styles to your CSS file and making sure your site’s HTML has the right classes, you can give users a smooth and nice-looking experience, no matter if they like light or dark mode.
Don’t forget to test your styles to make sure they change right based on what the user likes and that the code highlighting is easy to read and looks good in both themes.
Conclusion
We’ve set up beautiful, syntax-highlighted code blocks without adding any extra JavaScript for our readers. Have fun using Shiki, React Server Components, and Next.js in your projects.
Bring Your Ideas to Life 🚀
If you need help with a Laravel project let’s get in touch.
Lucky Media is proud to be recognized as a leading Next.js Development Agency
Technologies

Stay up-to-date
Be updated with all news, products and tips we share!

