Plate 64
Shiki vs Prism.js: 72 KB vs 8 KB, 36 ms vs 3 ms
Aditya Challa6 min read
I bundled both syntax highlighters on this box: Shiki 4.5.0 (JavaScript regex engine, TypeScript grammar, one theme) came out at 72,426 bytes gzip, while Prism.js 1.30.0 with its TypeScript component came out at 7,918 bytes. On a real 254-line TypeScript file, Shiki took about 35.7 ms per highlight versus about 3.1 ms for Prism, roughly 11.5× slower, but its tokens come from the same TextMate grammars VS Code uses.
Short answer: use Shiki when you highlight at build time (SSG, MDX, docs sites) and want editor-accurate colors with no client JavaScript. Keep Prism.js when highlighting has to run in the browser, on user input, or on a tight bundle budget. No affiliate links in this post.
What I tested
- Machine: 8 vCPU Intel Xeon / 15 GB RAM shared Linux cloud box, tested 6 Oct 2026 about 02:30 IST.
- Versions: shiki 4.5.0 (shiki/core + engine/javascript + @shikijs/langs/typescript + @shikijs/themes/github-dark), prismjs 1.30.0 (core bundle + prism-typescript), Node 22.20.0, esbuild 0.28.2 browser ESM minify + gzip -9.
- Input: undici 8.11.2's types/dispatcher.d.ts copied as the fixture (254 lines, 14,841 bytes of real-world TypeScript). Speed: 200 highlights per round, median of 9 rounds, two order-reversed processes. Scripts and JSON in /workspace/bench-shiki-prismjs/.
- Findings: Shiki 357,161 / 72,426 B vs Prism 21,199 / 7,918 B (minified / gzip). Per-file medians: Shiki 35.695 / 35.675 ms, Prism 3.085 / 3.099 ms. First highlight after load: Shiki 317–350 ms, Prism 12–16 ms. HTML output: Shiki 97,615 B with inline styles, Prism 85,028 B of class-only spans.
- One surprise: Prism wrapped the whole generic
Opts<T extends object = {}>inside one class-name span, soobjectgot no token of its own, and it leftsatisfiesas plain text. Shiki colored both as keywords/types the way VS Code does. - Not tested: Shiki's fine-grained bundles for many languages, transformers (line highlights, diff notation), rehype-pretty-code or Expressive Code wrappers, Prism plugins (line numbers, autoloader), or browser main-thread timing on a phone.
How much bigger is Shiki in the browser?
| Build (esbuild browser minify, TypeScript + 1 theme) | Minified | gzip -9 |
|---|---|---|
| Shiki 4.5.0, JavaScript regex engine | 357,161 B | 72,426 B |
| Shiki 4.5.0, Oniguruma WASM inlined | 927,650 B | 285,737 B |
| Prism.js 1.30.0 + TypeScript | 21,199 B | 7,918 B |
The JavaScript-engine build is about 9× Prism's gzip size, and the WASM route is far larger (onig.wasm alone is 148,439 B gzip). Prism still needs a theme stylesheet on top, which Shiki does not, because Shiki writes colors inline. Budget this the same way as in size-limit vs bundlesize. If the highlighter sits behind a Markdown pipeline, pair it with marked vs markdown-it.
Is Shiki too slow to highlight at runtime?
| 254-line TS file (median of 9 rounds × 200 files) | Shiki (JS engine) | Prism.js |
|---|---|---|
| Order Shiki then Prism (ms per file) | 35.695 | 3.085 |
| Order Prism then Shiki (ms per file) | 35.675 | 3.099 |
| Files per second | ~28 | ~323–324 |
| Import + setup (ms) | 36.6–46.0 | 11.1–11.3 |
| First highlight after setup (ms) | 316.7–350.2 | 12.3–15.7 |
A single extra run with the Oniguruma WASM engine landed at 29.653 ms per file, 79.2 ms setup and 210.6 ms first highlight, so it is faster per file than the JavaScript engine but much heavier to ship. At build time, 36 ms per code block is fine: 500 blocks cost about 18 seconds. In a live editor or comment preview, Prism's ~3 ms is the safer choice. Build tool choices that decide where that work happens sit in Vite vs Parcel and Rolldown vs Rollup.
Does the output differ?
| Topic | Shiki 4.5.0 | Prism.js 1.30.0 |
|---|---|---|
| Wrapper | full pre and code element | inner spans only, you add pre |
| Colors | inline style per token (theme baked in) | token classes, needs a CSS theme |
| Grammar source | TextMate (same as VS Code) | Prism regex grammars |
| Spans on my 2-line generics sample | 44 | 45 |
Opts<T extends object> | type name, keyword and type split | one class-name span, object untokenized |
satisfies operator | colored as a keyword | plain text |
Shiki's inline colors also mean dark/light switching needs its dual-theme option or CSS variables. Prism's classes are easier to restyle with your own CSS, which fits stacks built on UnoCSS vs Tailwind or Lightning CSS vs PostCSS. If you run Astro Starlight, you already get Shiki output: Starlight renders code blocks with Expressive Code, whose default highlighter is Shiki (both docs are linked in Sources). My Astro Starlight 0.42 notes cover that release's mobile menu and JS changes, not highlighting.
Which one should you pick?
| Situation | My pick |
|---|---|
| Static docs, blog or MDX highlighted at build time | Shiki |
| Colors must match VS Code exactly | Shiki |
| Highlighting in the browser on user input | Prism.js |
| Under 10 KB gzip for the highlighter | Prism.js |
| Many languages loaded lazily in the browser | Prism.js with its autoloader, or Shiki only if you accept the size |
Bottom line: Shiki 4.5.0 cost 72,426 B gzip and about 35.7 ms per 254-line file on this box, while Prism.js 1.30.0 cost 7,918 B and about 3.1 ms, but Shiki tokenized TypeScript generics correctly where Prism lumped them together. Who should not switch to Shiki: anyone highlighting in the client on every keystroke. Who should not stay on Prism: build-time docs sites that care about accurate TypeScript colors and want zero highlighter JavaScript on the page.
How this was made: I installed both packages on the ShopperCove box, bundled minimal TypeScript-only setups with esbuild, timed 200 highlights per round of a real 254-line declaration file in two order-reversed processes, and compared the HTML each library produced. The JSON results sit next to the scripts. The write-up was drafted with AI help and checked against that output.
Sources
- https://github.com/shikijs/shiki
- https://shiki.style/guide/bundles
- https://github.com/PrismJS/prism
- https://www.npmjs.com/package/shiki
- https://www.npmjs.com/package/prismjs
- https://starlight.astro.build/guides/authoring-content/
- https://expressive-code.com/key-features/syntax-highlighting/
Related
- https://www.shoppercove.com/blog/marked-vs-markdown-it
- https://www.shoppercove.com/blog/size-limit-vs-bundlesize
- https://www.shoppercove.com/blog/vite-vs-parcel
- https://www.shoppercove.com/blog/rolldown-vs-rollup
- https://www.shoppercove.com/blog/unocss-vs-tailwind
- https://www.shoppercove.com/blog/lightningcss-vs-postcss
- https://www.shoppercove.com/blog/astro-starlight-0-42-popover-js-dist-october-2026
- https://www.shoppercove.com/blog/svgo-vs-svgr
Lab evidence
What I found running this
Hands-on on ShopperCove box 6 Oct 2026 ~02:30 IST (8 vCPU Intel Xeon / 15 GB shared Linux, Node 22.20.0). shiki 4.5.0 (core + JS regex engine + TS lang + github-dark) vs prismjs 1.30.0 + prism-typescript, esbuild 0.28.2 browser ESM minify + gzip -9. Size: 357,161/72,426 B vs 21,199/7,918 B; Oniguruma WASM build 927,650/285,737 B. Speed 254-line TS file (undici dispatcher.d.ts), 200 highlights x 9-round median, order-reversed: Shiki 35.695/35.675 ms vs Prism 3.085/3.099 ms per file; WASM engine 29.653 ms. First highlight 316.7-350.2 vs 12.3-15.7 ms. HTML 97,615 B inline styles vs 85,028 B class spans. Prism left satisfies untokenized and lumped Opts<T extends object> into one class-name span. Not tested: many-language bundles, transformers, rehype-pretty-code/Expressive Code, Prism plugins, phone main-thread. No affiliate.