How to use Shiki with MDSveX for syntax highlighting in Svelte
Jan 31, 2024
Table of Contents
- Introduction
- Getting Started - Installing Shiki
- Setting up MDSveX
- Creating the Highlighter function using Shiki
- Results
Introduction
Just completed my portfolio website and was eager to incorporate blogs. After some searching, I discovered MDSveX, essentially MDX for Svelte. Once set up, I encountered a new challenge: how to elevate syntax highlighting. That’s when I came across Shiki.
Shiki is an exquisite syntax highlighter built on TextMate grammars, offering precision and potency. Powered by the same engine as VS Code, it supports syntax highlighting for nearly all languages supported by VS Code. Additionally, it operates during the build process, resulting in zero JavaScript overhead while achieving impeccable syntax highlighting.
Getting Started - Installing Shiki
First, we need to add Shiki as a dev dependency. Dev dependencies typically include tools and libraries utilized during development, but not necessary for the final production build. They often include testing frameworks, compilers, bundlers, and other development-specific utilities.
You can utilize your preferred package manager; I personally opt for pnpm.
pnpm i -D shiki
Setting up MDSveX
Firstly, let’s delve into the MDSveX documentation for a bit. The preprocessor function accepts an object of options that enable customization of your experience. Here, we’ll focus on utilizing the highlight
option.
To employ a custom highlight function, we’ll utilize the highlighter
property within the highlight
option. This function will receive two arguments: the code
to be highlighted and the lang
defined in the fenced code block, both as strings. This information will be essential later for highlighting our code using Shiki.
const mdsvexOptions = {
extensions: ['.svx'],
highlight: {
highlighter: customHighlightFunction
}
}
Creating the Highlighter function using Shiki
In our scenario, we’ll utilize the getHighlighter
function to instantiate a highlighter instance that can subsequently be passed into the highlight
option of the MDSveX preprocessor.
In svelte.config.js
, import the getHighlighter
function from shiki
.
import { getHighlighter } from 'shiki'
Additionally, we need to load a theme that we’ll utilize. In this instance, I’ve chosen to use the Dracula theme.
import { getHighlighter } from 'shiki'
import dracula from 'shiki/themes/dracula.mjs'
Now, let’s delve into the main section of the code, where we define the highlighter function. Here’s a breakdown of each step:
highlighter: async (code, lang) => {
const highlighter = await getHighlighter({ theme: dracula, langs: [lang] })
const html = highlighter.codeToHtml(code, { lang: lang, theme: dracula })
return `{@html `${html}`}`
}
Highlighter Function Creation: We’re defining an asynchronous arrow function that takes two parameters:
code
(the code to be highlighted) andlang
(the language of the code).Highlighter Instance Initialization: Within this function, we first create an instance of the highlighter using
getHighlighter
. This instance, namedhighlighter
in our code, is configured with the importeddracula
theme and an array containing thelang
parameter.Code Highlighting: Next, we use the
codeToHtml
method of thehighlighter
instance to transform thecode
into HTML with syntax highlighting applied. We pass in thelang
andtheme
parameters to ensure the correct highlighting theme and language.Returning Highlighted HTML: Finally, we return the highlighted HTML as a string, wrapped in
{@html \`${html}\`}
. This Svelte syntax allows the HTML to be rendered as raw HTML without being sanitized.
Results
With this setup, your svelte.config.js
file is now configured to use the custom highlighter function. You should have everything set up and ready to go! Here’s a snapshot of how your final svelte.config.js
should look:
import { getHighlighter } from 'shiki'
import dracula from 'shiki/themes/dracula.mjs'
const mdsvexOptions = {
extensions: ['.svx'],
highlight: {
highlighter: async (code, lang) => {
const highlighter = await getHighlighter({ theme: dracula, langs: [lang] })
const html = escapeSvelte(highlighter.codeToHtml(code, { lang: lang, theme: dracula }))
return `{@html `${html}`}`
}
},
remarkPlugins: [[remarkToc, {tight: true}], remarkUwrapImages],
rehypePlugins: [rehypeSlug]
}
/** @type {import('@sveltejs/kit').Config} */
const config = {
extensions: ['.svelte', '.svx'],
preprocess: [vitePreprocess(), mdsvex(mdsvexOptions)],
kit: {
adapter: adapter()
}
}
export default config
With this configuration, you’re all set to proceed with your Svelte project!