RK
12 : 47 AM

Hello, I'm

Rupsnigdha

Incorporating minimalism into the internet,

one site at a time.

How to use Shiki with MDSveX for syntax highlighting in Svelte

Jan 31, 2024

sveltekit

Table of Contents

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}`}`
}
  1. Highlighter Function Creation: We’re defining an asynchronous arrow function that takes two parameters: code (the code to be highlighted) and lang (the language of the code).

  2. Highlighter Instance Initialization: Within this function, we first create an instance of the highlighter using getHighlighter. This instance, named highlighter in our code, is configured with the imported dracula theme and an array containing the lang parameter.

  3. Code Highlighting: Next, we use the codeToHtml method of the highlighter instance to transform the code into HTML with syntax highlighting applied. We pass in the lang and theme parameters to ensure the correct highlighting theme and language.

  4. 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!

Heavily inspired from Brittany Chiang's Portfolio website. Coded in Visual Studio Code by yours truly. Built with SvelteKit and TailwindCSS, deployed with Vercel. The site uses GrandSlang Roman and Monserrat typefaces. Fonts made by Web Free Fonts is licensed by CC 4.0 BY.