Highlight your website's code blocks with VSCode
Tags: javascript, tutorial
Published: 2020-10-06 14:12:00

---
title: Highlight your website's code blocks with VSCode
description: Here's how you can use the VSCode Syntax Highlighter to highlight your website's code blocks
published: true
date: 2020-10-05 00:00:00 UTC
tags: [Javascript, Tutorial]
canonical_url: https://miguelpiedrafita.com/vscode-highlighting
cover_image: https://miguelpiedrafita.com/images/vscode-highlighting/cover.jpg
---

When it comes to highlighting code blocks on your website, it may seem there aren't many options available. I'd bet a 90% of websites use either [Prism](https://prismjs.com) or [HighlightJS](https://highlightjs.org/). While these options are good, they often miss the mark for more niche use-cases, like [supporting new PHP syntax](https://github.com/PrismJS/prism/pull/2566) or correctly highlighting Laravel Blade views, to the point where [some users opt to highlight the code manually](https://twitter.com/brendt_gd/status/1283381650806669312).

When I decided to improve the code blocks on this website, my first idea was to create a custom Prism theme, taking inspiration from the [VSCode Community Material theme](https://marketplace.visualstudio.com/items?itemName=Equinusocio.vsc-community-material-theme). After [almost 100 lines of hot-fixing Prism](https://gist.github.com/m1guelpf/a60c0b6e05fb88496833432199a95b46) however, I gave up and started looking for alternative solutions. I didn't have much hope, but ended up finding the [holy grail of syntax highlighting](https://github.com/shikijs/shiki): a way of _using the same highlighter that VSCode uses, on your website_, with support for any VSCode theme and extension.

Before I get started on how you can add this to your site too, there's a catch. The package used to achieve this needs to be run on the server, which means you need some sort of build process (although [there's an RFC open for browser support](https://github.com/shikijs/shiki/issues/91)). I'll be showing how to make this work with a simple node script, but I've had success getting it to work with Next.js and Gatsby, and I'm sure there's a way to get it working with other frameworks too. With that out of the way, let's get into it!

First, you'll need to create a node script that takes your markdown and turns it into HTML (which you can later echo from your website). For this example, we'll assume there's a `posts` directory full of markdown files and a `build` directory where our website expects html files to be.

```js
const fs = require('fs')
const path = require('path')
const shiki = require('shiki')
const markdown = require('markdown-it')

shiki.getHighlighter().then(highlighter => {
    const md = markdown({
        html: true,
        highlight: (code, lang) => highlighter.codeToHtml(code, lang)
    })

    fs.readdirSync(path.join(process.cwd(), 'posts')).filter(slug => /\.mdx?$/.test(slug)).forEach(slug => {
        const content = fs.readFileSync(path.join(process.cwd(), 'posts', slug), 'utf-8')

        fs.writeFileSync(path.join(process.cwd(), 'build', slug.replace(/\.mdx?$/, '.html')), md.render(content))

        console.log(`${slug} => ${slug.replace(/\.mdx?$/, '.html')}`)
    })
})
```

Now you just need to run that script during your build process and you'll get some awesome VSCode-powered code blocks!

Shiki comes with support for [plenty of languages by default](https://github.com/shikijs/shiki/blob/master/packages/languages/README.md#literal-values), but you can also add custom languages from any VSCode extension. You just need to find a `.tmLanguage` or `.tmLanguage.json` on the extension source, and copy it to your project. Then, you pass an array of additional languages to `shiki` when creating the highlighter.

```js
const langs = [
	{
		id: 'blade',
		scopeName: 'text.html.php.blade',
		path: path.join(process.cwd(), 'vendor/blade.tmLanguage.json'),
		embeddedLanguages: {
			'source.php': 'php',
			'source.css': 'css',
			'source.js': 'javascript',
		},
	},
	{
		language: 'dotenv',
		scopeName: 'source.env',
		path: path.join(process.cwd(), 'vendor/env.tmLanguage'),
		aliases: ['env'],
	},
]

shiki.getHighlighter({ langs }).then(highlighter => {
    //
})
```

Finally, what about custom themes? Again, shiki includes [many themes by default](https://github.com/shikijs/shiki/blob/master/packages/themes/README.md#literal-values), but you can bring your own by finding the JSON file for that theme in its source.

```js
const theme = shiki.loadTheme(path.join(process.cwd(), 'vendor/material-theme.json')),

shiki.getHighlighter({ theme }).then(highlighter => {
    //
})
```

Those are the basics, which you can then apply to your specific setup. Here's how I'm doing the highlighting with Next.js and `next-mdx-remote`.

```js
import matter from 'gray-matter'
import visit from 'unist-util-visit'
import renderToString from 'next-mdx-remote/render-to-string'

export const getStaticProps = async ({ params: { slug } }) => {
    const source = fs.readFileSync(path.join(process.cwd(), 'content/posts', `${slug}.mdx`))
    const { content, data } = matter(source)

    const shiki = await import('shiki')
	const highlighter = await shiki.getHighlighter({
		theme: shiki.loadTheme(path.join(process.cwd(), 'vendor/material-theme.json')),
		langs: [{
		    id: 'blade',
		    scopeName: 'text.html.php.blade',
		    path: path.join(process.cwd(), 'vendor/blade.tmLanguage.json'),
		    embeddedLanguages: { 'source.php': 'php', 'source.css': 'css', 'source.js': 'javascript' },
	    },],
	})

	const mdxSource = await renderToString(content, {
		mdxOptions: { remarkPlugins: [[remarkPlugin, { highlighter }]] },
		scope: data,
    })

	return { props: { slug, mdxSource, ...data } }
}

export const remarkPlugin = options => async tree => {
	visit(tree, 'code', node => {
		node.type = 'html'
		node.children = undefined
		node.value = options.highlighter.codeToHtml(node.value, node.lang).replace('<pre class="shiki"', `<pre class="shiki" language="${node.lang}" meta="${node.meta}"`)
	})
}
```

Did you add `shiki` to your blog? Are you using something different to highlight your code? [Reach out on Twitter](https://twitter.com/m1guelpf) and let me know!