Tale of the Tape: Highlight.js vs Shiki
Published: 2021-11-12 18:53:42

---
title: Tale of the Tape: Highlight.js vs Shiki
published: true
description: One of the best things about JavaScript is the ecosystem, as there always seems to be a package that can be used to accomplish your task. However, one of the worst things about JavaScript is that there are often many packages that you can use to achieve the same task. It’s not always obvious which package is best. How do we know which one to use?
tags: 
cover_image: https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qjruozwlwcp6e8c02639.jpg
canonical_url: https://blog.begin.com/posts/2021-11-09-tale-of-the-tape-highlightjs-vs-shiki
---

One of the best things about JavaScript is the ecosystem. There always seems to be a package to accomplish your task. However, one of the worst things about JavaScript is that many packages often do the same thing.

It’s not always obvious which package is best. How do we know which one to use? Do we judge by:

- GitHub stars
- npm downloads
- Package size
- Performance

Since we will be executing our code in an AWS Lambda function, we will want to keep the following [best practices](https://docs.aws.amazon.com/lambda/latest/dg/best-practices.html) in mind:

1. Minimize your deployment package size to its runtime necessities
2. Control the dependencies in your function's deployment package
3. Fast execution

This post will compare two popular packages that work with [markdown-it](https://www.npmjs.com/package/markdown-it) for syntax highlighting in JavaScript, [highlight.js](https://www.npmjs.com/package/highlight.js), and [shiki](https://www.npmjs.com/package/shiki).

## Popularity

First, let's take a look at each package's popularity.

highlight.js | Popularity | shiki
--: | :-: | :--
5,526,141 | weekly npm downloads | 219,361
19.1k | GitHub stars | 2.7k

In this case, highlight.js is the runaway winner, but popularity is not the best way to compare packages. Checking in with both packages' GitHub repositories, you can tell that both are under active development and have had new releases in the past month.

That didn't tell us too much, so let's head over to the weigh-in.

## Weight

Using [BundlePhobia](https://bundlephobia.com/) to check the bundle size, shiki comes out on top as it is the lighter package.

highlight.js | Weight | shiki
--: | :-: | :--
886.4kB | bundle size | 108.4kB
279.5kB | bundle size + gzip'd | 30.9kB
5.59s | download time (slow 3G) | 0.62s
319ms | download time (4G) | 35ms

But let's go a bit deeper by running `slow-deps`. This command will show you the size and install time of each package, including its dependencies.

```bash
npx slow-deps
Analyzing 2 dependencies...
[====================] 100% 0.0s
------------------------------------------
| Dependency   | Time  | Size   | # Deps |
------------------------------------------
| shiki        | 1.5s  | 9.1 MB | 6      |
| highlight.js | 824ms | 3.9 MB | 1      |
------------------------------------------
```

While shiki starts off looking good in this category, highlight.js comes back strong as it doesn't have any external dependencies, while shiki has five increasing its unpacked size to 9.1 MB.

## Functionality

Okay, highlight.js is out to an early lead, but how well do these two packages work? We'll run the same text through both packages and see what happens.

### highlight.js

The following code:

```js
const hljs = require('highlight.js/lib/common');

const html = hljs.highlight(`// async
let arc = require ('@architect/functions')
exports. handler = arc. events. subscribe (handler)
async function handler (event) {
console. log (event)
return
}`, {language: 'js'}).value

console.log(html)
```

produces the following output when viewed in a browser.

![highlight.js](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8245rfp6tvc5k6xda37u.png)

### shiki

While similar code using shiki:

```js
const shiki = require('shiki')

shiki
  .getHighlighter({theme: 'nord'})
  .then(highlighter => {
    let html = highlighter.codeToHtml(`// async
    let arc = require ('@architect/functions')
    exports. handler = arc. events. subscribe (handler)
    async function handler (event) {
    console. log (event)
    return
    }`, 'js')
    console.log(html)
  })

```

It looks like this when viewed in a browser.

![shiki](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7sdleobspsx6x1hqqoyi.png)

![What's the difference?](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hsaxxjrn1ayu78jixnvh.jpg)

I'll forgive you if it doesn't jump out at you, but the `exports` keyword is colored differently in highlight.js and shiki. Other than that, both packages do a great job of highlighting the code using our Atom One Dark theme. Advantage, no one.

## Speed

Finally, let's compare the speed of the two packages. We'll add [console.time](https://developer.mozilla.org/en-US/docs/Web/API/console/time) to our above code to profile how long each package takes to execute.

highlight.js | Speed | shiki
--: | :-: | :--
6.588ms | time | 312.826ms
$0.0000000147 | cost | $0.0000006573
$0.0147 | cost per 1M executions | $0.6573

And here is the point where highlight.js lands the knockout punch. Highlight.js ends up being 44 times faster than shiki. Speed is essential to us for two reasons:

1. We deliver the HTML via Lambda functions, and we want the quickest execution time possible to reduce the [time to first byte](https://developer.mozilla.org/en-US/docs/Glossary/time_to_first_byte).
2. Lambdas are billed in milliseconds, so lower execution times will help keep the cost low.

## Conclusion

After evaluating the two packages, it was clear that highlight.js is the best package for our use case, coming out on top in three of the four categories we evaluated.

highlight.js | Category | shiki
--: | :-: | :--
🟢 | Popularity | 🔴
🟢 | Weight | 🔴
🟡 | Functionality | 🟡
🟢 | Speed | 🔴

That doesn't mean you shouldn't use shiki as your use case may be different than ours. For instance, if you are doing static site generation (SSG), you may not be as concerned about the overall build time as your end-user will never see that delay.

We are trying to highlight (pun intended) that it pays to do your due diligence when choosing a package. The most popular package may not end up being the best choice, and you may see wild swings in performance that could affect your final choice.