Skip to main content

Shiki vs Prism vs highlight.js: Syntax Highlighting Libraries (2026)

·PkgPulse Team

TL;DR

Shiki uses VS Code's TextMate grammars — accurate, beautiful highlighting with VS Code themes, renders to HTML at build time, no client-side JavaScript needed, used by VitePress, Astro, and Nuxt Content. Prism is the lightweight client-side highlighter — extensible plugin system, token-based, 300+ languages, used by MDN and Gatsby. highlight.js is the classic auto-detecting highlighter — automatically detects language, 190+ languages, no dependencies, the most widely used. In 2026: Shiki for static/server-rendered docs, Prism for client-side with plugins, highlight.js for zero-config highlighting.

Key Takeaways

  • Shiki: ~5M weekly downloads — VS Code grammars, server-side, zero client JS
  • Prism: ~5M weekly downloads — lightweight, plugins (line numbers, copy), client-side
  • highlight.js: ~10M weekly downloads — auto-detect language, zero-config, universal
  • Shiki produces the most accurate highlighting (same engine as VS Code)
  • Prism has the best plugin ecosystem (line highlighting, diff, toolbar)
  • highlight.js is the easiest to set up (script tag + auto-detection)

Shiki

Shiki — VS Code-powered highlighting:

Basic usage

import { codeToHtml } from "shiki"

const html = await codeToHtml('console.log("Hello, World!")', {
  lang: "javascript",
  theme: "one-dark-pro",
})

console.log(html)
// → <pre class="shiki one-dark-pro" style="background-color:#282c34">
// →   <code><span style="color:#E5C07B">console</span>...
// → </pre>

// Output is fully styled HTML — no client-side JS needed!

Multiple themes (light/dark)

import { codeToHtml } from "shiki"

// Dual theme — light + dark mode:
const html = await codeToHtml('const x = 42', {
  lang: "typescript",
  themes: {
    light: "github-light",
    dark: "github-dark",
  },
})

// CSS to switch between themes:
// @media (prefers-color-scheme: dark) {
//   .shiki { background-color: var(--shiki-dark-bg) !important; }
//   .shiki span { color: var(--shiki-dark) !important; }
// }

Highlighter instance (performance)

import { createHighlighter } from "shiki"

// Create reusable highlighter (loads grammars once):
const highlighter = await createHighlighter({
  themes: ["one-dark-pro", "github-light"],
  langs: ["javascript", "typescript", "python", "rust", "json"],
})

// Fast repeated highlighting:
const html1 = highlighter.codeToHtml('const x = 1', { lang: "ts", theme: "one-dark-pro" })
const html2 = highlighter.codeToHtml('def main():', { lang: "python", theme: "one-dark-pro" })
const html3 = highlighter.codeToHtml('fn main() {}', { lang: "rust", theme: "one-dark-pro" })

// Dispose when done:
highlighter.dispose()

Transformers (line highlighting, diff, etc.)

import { codeToHtml } from "shiki"
import {
  transformerNotationDiff,
  transformerNotationHighlight,
  transformerNotationFocus,
} from "@shikijs/transformers"

const code = `
const old = "removed"  // [!code --]
const new = "added"    // [!code ++]
const highlighted = 1  // [!code highlight]
const focused = true   // [!code focus]
`

const html = await codeToHtml(code, {
  lang: "typescript",
  theme: "one-dark-pro",
  transformers: [
    transformerNotationDiff(),      // Red/green diff lines
    transformerNotationHighlight(), // Highlighted lines
    transformerNotationFocus(),     // Focus with dimmed context
  ],
})

Framework integrations

// VitePress — built-in Shiki:
// ```ts {1,3-4}  ← line highlighting
// const a = 1  // [!code ++]  ← diff
// ```

// Astro — built-in Shiki:
// astro.config.mjs
export default defineConfig({
  markdown: {
    shikiConfig: {
      theme: "one-dark-pro",
      wrap: true,
    },
  },
})

// Nuxt Content — built-in Shiki:
// nuxt.config.ts
export default defineNuxtConfig({
  content: {
    highlight: {
      theme: "github-dark",
      langs: ["js", "ts", "vue", "css", "html"],
    },
  },
})

Prism

Prism — lightweight client-side highlighter:

Basic setup

<!-- CDN (quickest setup): -->
<link href="https://cdn.jsdelivr.net/npm/prismjs/themes/prism-tomorrow.css" rel="stylesheet" />
<script src="https://cdn.jsdelivr.net/npm/prismjs/prism.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/prismjs/components/prism-typescript.min.js"></script>

<!-- Your code blocks: -->
<pre><code class="language-typescript">
const greeting: string = "Hello, World!"
console.log(greeting)
</code></pre>

<!-- Prism auto-highlights on page load -->

Node.js / programmatic

import Prism from "prismjs"
import "prismjs/components/prism-typescript"
import "prismjs/components/prism-python"
import "prismjs/components/prism-rust"

const code = 'const x: number = 42'
const html = Prism.highlight(code, Prism.languages.typescript, "typescript")

console.log(html)
// → <span class="token keyword">const</span>
// → <span class="token literal-property">x</span>
// → <span class="token operator">:</span> ...

// Wrap in <pre><code>:
const block = `<pre class="language-typescript"><code>${html}</code></pre>`

Plugins

<!-- Line numbers: -->
<script src="https://cdn.jsdelivr.net/npm/prismjs/plugins/line-numbers/prism-line-numbers.min.js"></script>
<link href="https://cdn.jsdelivr.net/npm/prismjs/plugins/line-numbers/prism-line-numbers.css" rel="stylesheet" />
<pre class="line-numbers"><code class="language-js">...</code></pre>

<!-- Line highlight: -->
<script src="https://cdn.jsdelivr.net/npm/prismjs/plugins/line-highlight/prism-line-highlight.min.js"></script>
<pre data-line="2,4-6"><code class="language-js">...</code></pre>

<!-- Copy to clipboard: -->
<script src="https://cdn.jsdelivr.net/npm/prismjs/plugins/toolbar/prism-toolbar.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/prismjs/plugins/copy-to-clipboard/prism-copy-to-clipboard.min.js"></script>

<!-- Diff highlight: -->
<pre><code class="language-diff-typescript diff-highlight">
- const old = "removed"
+ const new = "added"
</code></pre>

Themes

Built-in themes:
  prism.css             — Default (light)
  prism-dark.css        — Dark
  prism-funky.css       — Funky
  prism-okaidia.css     — Okaidia (Monokai)
  prism-twilight.css    — Twilight
  prism-coy.css         — Coy
  prism-solarizedlight.css — Solarized Light
  prism-tomorrow.css    — Tomorrow Night

Community themes (prism-themes package):
  prism-vsc-dark-plus    — VS Code Dark+
  prism-one-dark         — Atom One Dark
  prism-dracula          — Dracula
  prism-nord             — Nord
  prism-material-dark    — Material Dark

highlight.js

highlight.js — auto-detecting highlighter:

Basic setup

<!-- CDN (simplest possible setup): -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/highlight.js/styles/github-dark.min.css" />
<script src="https://cdn.jsdelivr.net/npm/highlight.js/highlight.min.js"></script>
<script>hljs.highlightAll()</script>

<!-- Code blocks — language auto-detected! -->
<pre><code>
const greeting = "Hello, World!"
console.log(greeting)
</code></pre>
<!-- highlight.js detects this as JavaScript automatically -->

Node.js / programmatic

import hljs from "highlight.js"

// Auto-detect language:
const result = hljs.highlightAuto('const x = 42')
console.log(result.language) // → "javascript"
console.log(result.value)    // → highlighted HTML

// Specify language:
const result2 = hljs.highlight('const x: number = 42', { language: "typescript" })
console.log(result2.value)
// → <span class="hljs-keyword">const</span>
// → <span class="hljs-attr">x</span>: ...

// Only load specific languages (smaller bundle):
import hljs from "highlight.js/lib/core"
import javascript from "highlight.js/lib/languages/javascript"
import typescript from "highlight.js/lib/languages/typescript"

hljs.registerLanguage("javascript", javascript)
hljs.registerLanguage("typescript", typescript)

Themes

Built-in themes (300+):

Popular dark themes:
  github-dark.css
  atom-one-dark.css
  vs2015.css          — VS Code dark
  monokai.css
  dracula.css
  nord.css
  tokyo-night-dark.css

Popular light themes:
  github.css
  atom-one-light.css
  vs.css              — VS Code light
  stackoverflow-light.css
  xcode.css

// Use theme:
<link rel="stylesheet" href="highlight.js/styles/github-dark.css" />

Language auto-detection

import hljs from "highlight.js"

// Auto-detect works by scoring each language:
const results = [
  hljs.highlightAuto('print("hello")'),           // → python
  hljs.highlightAuto('console.log("hello")'),      // → javascript
  hljs.highlightAuto('fn main() { println!("hello"); }'), // → rust
  hljs.highlightAuto('SELECT * FROM users'),       // → sql
]

results.forEach((r) => {
  console.log(`${r.language}: confidence ${r.relevance}`)
})

// Limit detection to specific languages:
const result = hljs.highlightAuto(code, ["javascript", "typescript", "python"])

With Markdown renderers

import MarkdownIt from "markdown-it"
import hljs from "highlight.js"

const md = new MarkdownIt({
  highlight(str, lang) {
    if (lang && hljs.getLanguage(lang)) {
      return hljs.highlight(str, { language: lang }).value
    }
    return hljs.highlightAuto(str).value
  },
})

const html = md.render("```typescript\nconst x = 42\n```")

Feature Comparison

FeatureShikiPrismhighlight.js
EngineTextMate (VS Code)Custom tokenizerCustom parser
RenderingServer-side (HTML)Client-side (DOM)Client/server
Client JS needed
Language auto-detect
Languages200+300+190+
ThemesVS Code themesCSS themes300+ CSS themes
Light/dark toggle✅ (CSS vars)✅ (swap CSS)✅ (swap CSS)
Line numbersVia transformer✅ (plugin)✅ (plugin)
Line highlighting✅ (transformer)✅ (plugin)
Diff highlighting✅ (transformer)✅ (plugin)
Copy buttonVia transformer✅ (plugin)
AccuracyHighest (VS Code)GoodGood
Bundle size0 (server-side)~20KB + langs~30KB + langs
Used byVitePress, AstroMDN, GatsbyMany CMS/blogs
Weekly downloads~5M~5M~10M

When to Use Each

Use Shiki if:

  • Building a static site or documentation (VitePress, Astro, Nuxt)
  • Want the most accurate highlighting (VS Code quality)
  • Need zero client-side JavaScript
  • Want VS Code themes (One Dark Pro, GitHub Dark, etc.)
  • Need line highlighting, diff, or focus transformers

Use Prism if:

  • Need client-side highlighting with plugins
  • Want line numbers, copy button, toolbar out of the box
  • Building a CMS or user-facing code editor
  • Need the largest language support (300+ languages)
  • Prefer CSS-based theming

Use highlight.js if:

  • Want the simplest setup (script tag + auto-highlight)
  • Need automatic language detection
  • Building a blog or content site with user-submitted code
  • Want the largest theme collection
  • Need Markdown renderer integration

Methodology

Download data from npm registry (weekly average, February 2026). Feature comparison based on Shiki v1.x, Prism v1.x, and highlight.js v11.x.

Compare developer tooling and documentation utilities on PkgPulse →

Comments

Stay Updated

Get the latest package insights, npm trends, and tooling tips delivered to your inbox.