# Technical Documentation: shikijs/shiki

> ℹ️ **Provenance:** Multi-Tier Ingestion · [CodeWiki Reference](https://codewiki.google/github.com/shikijs/shiki)

### Overview (`README.md`)

<img src="https://raw.githubusercontent.com/shikijs/shiki/main/docs/public/logo.svg" width="150" alt="Shiki Logo" />

# Shiki 式

[](https://www.npmjs.com/package/shiki)
[](https://www.npmjs.com/package/shiki)
[](https://app.codecov.io/gh/shikijs/shiki/tree)

A beautiful syntax highlighter based on TextMate grammar, accurate and powerful.

[📚 **Documentation**](https://shiki.style)

## Branch

The main branch is currently at _v4.x_.

| Branch                                         | Description                        |
| ---------------------------------------------- | ---------------------------------- |
| [v3](https://github.com/shikijs/shiki/tree/v3) | v3.23 of Shiki                     |
| [v2](https://github.com/shikijs/shiki/tree/v2) | v2.5 of Shiki                      |
| [v1](https://github.com/shikijs/shiki/tree/v1) | v1.29 of Shiki                     |
| [v0](https://github.com/shikijs/shiki/tree/v0) | v0.14 of Shiki, the legacy version |

## License

[MIT](./LICENSE)

---

### Overview (`packages/cli/README.md`)

# shiki-cli

Shiki in the command line.

[Documentation](https://shiki.style/packages/cli)

## License

MIT

---

### Overview (`packages/codegen/README.md`)

# @shikijs/codegen

Codegen for fine-grained Shiki bundles.

[Documentation](https://shiki.style/packages/codegen)

## License

MIT

---

### Overview (`packages/colorized-brackets/README.md`)

# @shikijs/colorized-brackets

VSCode-style colorized brackets transformer for [Shiki](https://github.com/shikijs/shiki).

[Documentation](https://shiki.style/packages/colorized-brackets)

## License

MIT

---

### Overview (`packages/core/README.md`)

# @shikijs/core

The core functionality of [Shiki](https://github.com/shikijs/shiki), without any grammar of themes bundled.

It's the same as importing `shiki/core`.

---

### Overview (`packages/engine-javascript/README.md`)

# @shikijs/engine-javascript

Engine for Shiki using JavaScript's native RegExp. Uses [Oniguruma-To-ES](https://github.com/slevithan/oniguruma-to-es) to transpile regex syntax and behavior.

[Documentation](https://shiki.style/guide/regex-engines)

## License

MIT

---

### Overview (`packages/engine-oniguruma/README.md`)

# @shikijs/engine-oniguruma

Engine for Shiki using Oniguruma RegExp engine in WebAssembly.

[Documentation](https://shiki.style/guide/regex-engines)

## License

MIT

---

### Overview (`packages/langs-precompiled/README.md`)

# @shikijs/langs-precompiled

Precompiled languages for Shiki that can runs in pure JavaScript environment.

Requires ES2024+ environment.

> **Experimental**

## Unsupported Languages



## License

MIT

---

### Overview (`packages/langs/README.md`)

# @shikijs/langs

TextMate grammars for Shiki.

## License

MIT

---

### Overview (`packages/magic-move/README.md`)

# @shikijs/magic-move

Smoothly animated code blocks with [shiki](https://github.com/shikijs/shiki). Useful for code-step animations in slide decks (e.g. [Slidev](https://sli.dev/guide/syntax#shiki-magic-move)) and tutorials.

[Documentation](https://shiki.style/packages/magic-move)

## License

MIT

---

### Overview (`packages/markdown-exit/README.md`)

# @shikijs/markdown-exit

[markdown-exit](https://github.com/serkodev/markdown-exit) plugin for [shiki](https://github.com/shikijs/shiki)

[Documentation](https://shiki.style/packages/markdown-exit)

## License

MIT

---

### Overview (`packages/markdown-it/README.md`)

# @shikijs/markdown-it

[markdown-it](https://markdown-it.github.io/) plugin for [shiki](https://github.com/shikijs/shiki)

[Documentation](https://shiki.style/packages/markdown-it)

## License

MIT

---

### Overview (`packages/monaco/README.md`)

# @shikijs/monaco

Use [shiki](https://github.com/shikijs/shiki) to highlight [Monaco Editor](https://microsoft.github.io/monaco-editor/).

[Documentation](https://shiki.style/packages/monaco)

## License

MIT

---

### Overview (`packages/rehype/README.md`)

# rehype-shiki

[rehype](https://github.com/rehypejs/rehype) plugin for [shiki](https://github.com/shikijs/shiki).

[Documentation](https://shiki.style/packages/rehype)

## License

MIT

---

### Overview (`packages/shiki/README.md`)

<img src="https://raw.githubusercontent.com/shikijs/shiki/main/docs/public/logo.svg" width="150" alt="Shiki Logo" />

# Shiki 式

[](https://www.npmjs.com/package/shiki)
[](https://www.npmjs.com/package/shiki)
[](https://app.codecov.io/gh/shikijs/shiki)

A beautiful syntax highlighter based on TextMate grammars, accurate and powerful.

[📚 **Documentation**](https://shiki.style)

## License

[MIT](./LICENSE)

---

### Overview (`packages/stream/README.md`)

# @shikijs/stream

Streaming colorization for [shiki](https://github.com/shikijs/shiki). Useful for highlighting text streams like LLM outputs.

[Documentation](https://shiki.style/packages/stream)

## License

MIT

---

### Overview (`packages/themes/README.md`)

# @shikijs/themes

TextMate themes for Shiki.

## License

MIT

---

### Overview (`packages/transformers/README.md`)

# @shikijs/transformers

Common transformers for [shiki](https://github.com/shikijs/shiki), inspired by [shiki-processor](https://github.com/innocenzi/shiki-processor).

[Documentation](https://shiki.style/packages/transformers)

## License

MIT

---

### Overview (`packages/twoslash/README.md`)

# @shikijs/twoslash

A [shiki](https://github.com/shikijs/shiki) transformer for [TypeScript Twoslash](https://www.typescriptlang.org/dev/twoslash/).

[Documentations](https://shiki.style/packages/twoslash)

## License

MIT

---

### Overview (`packages/types/README.md`)

# @shikijs/types

Types for Shiki.

## License

MIT

---

### Overview (`packages/vitepress-twoslash/README.md`)

# @shikijs/vitepress-twoslash

Enable Twoslash support in VitePress.

[Documentation](https://shiki.style/packages/vitepress#twoslash)

## License

MIT

---

### Index (`docs/index.md`)

---
layout: home

hero:
  name: "Shiki 式 "
  text: "Syntax highlighter"
  tagline: A beautiful yet powerful syntax highlighter
  image:
    src: /logo.svg
    alt: Shiki Logo
  actions:
    - theme: brand
      text: Get Started
      link: /guide/
    - theme: alt
      text: Installation
      link: /guide/install/

features:
  - title: Accurate & Beautiful
    icon: 🌈
    details: TextMate grammar powered, same engine as your VS Code. Improves with VS Code.
  - title: Zero Runtime
    icon: ⏱️
    details: Runs ahead of time, ship zero JavaScript while getting the perfect syntax highlighting.
  - title: Customizable
    icon: 🧩
    details: HAST based. Addons, transformations, highly customizable.
  - title: ESM & Universal
    icon: 🎄
    details: Fully tree-shakable ESM, runs on any JavaScript runtime, Browser, Node.js, Cloudflare Workers, etc.
---

<HomeDemo />

---

### Guide Index (`docs/guide/index.md`)

---
outline: deep
---

# Introduction

<br>

<span text-brand-yellow text-xl>Shiki</span> <span op75>(式, a Japanese word for ["Style"](https://jisho.org/word/%E5%BC%8F))</span> is a beautiful and powerful syntax highlighter based on TextMate grammar and themes, the same engine as VS Code's syntax highlighting. Provides very accurate and fast syntax highlighting for almost any mainstream programming language.

No custom RegExp to maintain, no custom CSS to maintain, no custom HTML to maintain. And as your favorite languages and themes in VS Code evolve - your syntax highlighting will evolve too.

Oh by the way, all the code blocks in this site are highlighted by Shiki, as you'd expect \:)

## Features

- All grammars/themes/wasm served as ESM, lazy-loaded on demand and bundler-friendly.
- Portable & agnostic. Does not rely on Node.js APIs or the filesystem, works in any modern JavaScript runtime.
- ESM-only ([CDN Usage](/guide/install#cdn-usage), [CJS Usage](/guide/install#cjs-usage)).
- [Bundles languages/themes composedly](/guide/bundles#fine-grained-bundle).
- [Light/Dark themes support](/guide/dual-themes)
- [`hast` support](/guide/transformers#codetohast)
- [Transformers API](/guide/transformers)
- [Decorations API](/guide/decorations)
- [TypeScript Twoslash integrations](/packages/twoslash)
- [Compatible build](/guide/compat)

## Playground

Here is a little playground for you to try out how Shiki highlights your code. The other code blocks in this docs are rendered on the build time and ships statically, while this playground is rendered on the client side in the browser. Themes and languages are loaded on demand.

<ShikiMiniPlayground />

[Install Shiki](/guide/install) to use it in your project.

## Bundle Size

You can inspect the bundle size in detail on [pkg-size.dev/shiki](https://pkg-size.dev/shiki).

As of `v1.1.6`, measured at 22nd, February 2024:

| Bundle              | Size (minified) | Size (gzip) | Notes                                                            |
| ------------------- | --------------: | ----------: | ---------------------------------------------------------------- |
| `shiki`             |          6.9 MB |      1.3 MB | All themes and languages as async chunks                         |
| `shiki/bundle/full` |          6.9 MB |      1.3 MB | Same as `shiki`                                                  |
| `shiki/bundle/web`  |          4.2 MB |      748 KB | All themes and common web languages as async chunks              |
| `shiki/core`        |          106 KB |       34 KB | Core engine without any themes or languages, compose on your own |
| `shiki/wasm`        |          623 KB |      231 KB | WASM binary inlined as base64 string                             |

---

### Api (`docs/api.md`)

# API References

## `codeToHast`

You can also get the intermediate `hast` to do custom rendering without serializing them into HTML with `codeToHast`. You can also further integrate the AST with the [unified](https://github.com/unifiedjs) ecosystem.

```
/* Detailed source-code truncated for AI context efficiency. */
```



```ts
{
  type: 'root',
  children: [
    {
      type: 'element',
      tagName: 'pre',
      properties: {
        class: 'shiki vitesse-light',
        style: 'background-color:#ffffff;color:#393a34',
        tabindex: '0'
      },
      children: [
        {
          type: 'element',
          tagName: 'code',
          properties: {},
          children: [
            {
              type: 'element',
              tagName: 'span',
              properties: { class: 'line' },
              children: [
                {
                  type: 'element',
                  tagName: 'span',
                  properties: { style: 'color:#AB5959' },
                  children: [ { type: 'text', value: 'const' } ]
                },
                {
                  type: 'element',
                  tagName: 'span',
                  properties: { style: 'color:#B07D48' },
                  children: [ { type: 'text', value: ' a' } ]
                },
                {
                  type: 'element',
                  tagName: 'span',
                  properties: { style: 'color:#999999' },
                  children: [ { type: 'text', value: ' =' } ]
                },
                {
                  type: 'element',
                  tagName: 'span',
                  properties: { style: 'color:#2F798A' },
                  children: [ { type: 'text', value: ' 1' } ]
                }
              ]
            }
          ]
        }
      ]
    }
  ]
}
```

---

### Languages (`docs/languages.md`)

# Languages

## Bundled Languages

Language grammars listed below are re-distributed via [`tm-grammars`](https://github.com/shikijs/textmate-grammars-themes/tree/main/packages/tm-grammars) into the `shiki` package.

> [!NOTE]
> Shiki does NOT control/maintain the grammars. If you want to contribute to the grammars, please refer to [`textmate-grammars-themes`](https://github.com/shikijs/textmate-grammars-themes#contribute).

<LanguagesList />

Grammars are covered by their repositories's respective licenses, which are permissive (apache-2.0, mit, etc), and made available in [this NOTICE](https://github.com/shikijs/textmate-grammars-themes/blob/main/packages/tm-grammars/NOTICE).

For loading your custom languages, please reference to [this guide](/guide/load-lang).

## Special Languages

### Plain Text

You can set lang to `text` to bypass highlighting. This is useful as the fallback when you receive user specified language that are not available. For example:

```txt
import { codeToHtml } from 'shiki'

const html = codeToHtml('console.log("Hello World")', {
  lang: 'text', // [!code hl]
  theme: 'vitesse-light',
})
```

`txt`, `plain` are provided as aliases to `text` as well.

### ANSI

A special processed language `ansi` is provided to highlight terminal outputs. For example:

```ansi
[0;32mcolored foreground[0m
[0;42mcolored background[0m

[0;1mbold text[0m
[0;2mdimmed text[0m
[0;4munderlined text[0m
[0;7mreversed text[0m
[0;9mstrikethrough text[0m
[0;4;9munderlined + strikethrough text[0m
```

Check the [raw markdown of code snippet above](https://github.com/shikijs/shiki/blob/main/docs/languages.md?plain=1#L35).

---

### Package.Json (`docs/package.json`)

{
  "type": "module",
  "private": true,
  "scripts": {
    "docs:dev": "vitepress dev",
    "docs:build": "vitepress build",
    "docs:preview": "vitepress preview"
  },
  "devDependencies": {
    "@braintree/sanitize-url": "catalog:docs",
    "@unocss/reset": "catalog:docs",
    "@vueuse/core": "catalog:docs",
    "cytoscape": "catalog:docs",
    "cytoscape-cose-bilkent": "catalog:docs",
    "dayjs": "catalog:docs",
    "debug": "catalog:docs",
    "floating-vue": "catalog:docs",
    "fuse.js": "catalog:docs",
    "pinia": "catalog:cli",
    "tm-grammars": "catalog:inlined",
    "tm-themes": "catalog:inlined",
    "unocss": "catalog:docs",
    "unplugin-vue-components": "catalog:docs",
    "vitepress": "catalog:docs",
    "vitepress-plugin-group-icons": "catalog:docs",
    "vue": "catalog:docs"
  }
}

---

### Themes (`docs/themes.md`)

# Themes

## Bundled Themes

Themes listed below are re-distributed via [`tm-themes`](https://github.com/shikijs/textmate-grammars-themes/tree/main/packages/tm-themes) into the `shiki` package.

> [!NOTE]
> Shiki does NOT control/maintain the themes. If you want to contribute to the themes, please refer to [`textmate-grammars-themes`](https://github.com/shikijs/textmate-grammars-themes#contribute).

<ThemesList />

Themes are covered by their repositories' respective licenses, which are permissive (apache-2.0, mit, etc), and made available in [this NOTICE](https://github.com/shikijs/textmate-grammars-themes/blob/main/packages/tm-themes/NOTICE).

For loading your custom themes, please reference to [this guide](/guide/load-theme).

## Special Themes

You can set theme to `none` to bypass highlighting. This is useful as the fallback when you receive user specified theme names that are not available. For example:

```ts twoslash theme:none
// @twoslash-cache: {"v":1,"hash":"86ec614e5e60566b55ed222e8e581ca015d4e3a6c7f2bd3716fca6224c3c0fda","data":"N4Igdg9gJgpgziAXAbVAFwJ4AcZJACwgDcYAnEAGhDRgA808AKAQwBsBLZuASgAIBjCGDhoB0GABUIACTQBbVol6NBsJSNLswAcwq8IWNOyFwlAYXFTpXNAHlDx4QB4AQgFcwUVjCgAZZjpuzNoweu6e3lAS+DByMAB8fAC88bwACqQQcuxwME4aWtrxADpg7HJYEKSiqpIy8qyUICLM1UgAnFTeOmj4SACMAAxUaK0hDIggtVYNTRxguIjDU/itzPw05IjtAL4U6NiLBMRkTTT0eILCovgNShlZOXkFOvFNLW2IACwATF0wPT63xGYxgEwIsy6WkWfxWaw2p0QADY9gccHhCCRyCM6OCWBwuHxpvUFCpxOo0JodHoDEYTOZLDIbPY6c5wl4fP5AsFQrx2ZForEEtx7plsrl8pTCiUyhUqjVGbIFO9Rp8vp0QN1tL0BstVeNLorIZroUhYfxVqR1psBgBmVHUQ4Yk7Y6i4phYTI4aoYPisALaJTFEAAK2YRC4/E0hmDKtaE3aX3+gKQSeooPB/p0c1NiFtVAt8JtiB+Dsw6MmmNOOIuk0YnoMZEwfAssBZjjgAGVCt5BXFXB4OVEYnF4gA6XpCpR9mAAJRg2hylOYrIAgmAMLwAD68TtSnS+dibNgAVTKQgHER8M70LyKcc+/X6SOT2qBAFYQaQDZNJ3EcwsqYFpa1qIqWAC6BbiHg5SVNUvDAGIsAzAovA7LwABmYq8AA5HA+DsAA1uwOGlKUVwiLwtyoUkSF1EqrCMDhFEQN4Y6sBA2iMMG0gwKwHG8AA6lUrBQMG3A4XowClLwvBZoGuFhhGcBRuwhiSTJvB/jASg4ZACySbwAD0Rm8MgACEtRUaw4GlDs3BNHEoxIKAuIAnAjh4GgCA7DsQA"}
import { codeToHtml } from 'shiki'

const html = codeToHtml('console.log("Hello World")', {
  lang: 'javascript',
  theme: 'none', // [!code hl]
})
```

---

### Tsconfig.Json (`docs/tsconfig.json`)

{
  "extends": "../tsconfig.json",
  "compilerOptions": {
    "target": "ESNext",
    "module": "esnext",
    "moduleResolution": "Bundler",
    "strict": true
  }
}

---

### Blog V2 (`docs/blog/v2.md`)

---
outline: deep
---

# Shiki v2.0.0

Shiki v2.0.0 itself is a **boring** release.

In case you missed, there are quite some cool new features we have landed in minor releases progressively:

| Version                                                          | Noteable New Features                                                                                                                                 |
| ---------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- |
| [v1.1.0](https://github.com/shikijs/shiki/releases/tag/v1.1.0)   | Better Twoslash support                                                                                                                               |
| [v1.3.0](https://github.com/shikijs/shiki/releases/tag/v1.3.0)   | New `structure: inline` option                                                                                                                        |
| [v1.6.0](https://github.com/shikijs/shiki/releases/tag/v1.6.0)   | Scoped color replacement, thanks to [@QuentinRoy](https://github.com/QuentinRoy)                                                                      |
| [v1.8.0](https://github.com/shikijs/shiki/releases/tag/v1.8.0)   | Expose `.dispose()` method for explicit resource cleanup                                                                                              |
| [v1.10.0](https://github.com/shikijs/shiki/releases/tag/v1.10.0) | Introduced [Grammar State](/guide/grammar-state) for partial code highlighting                                                                        |
| [v1.15.0](https://github.com/shikijs/shiki/releases/tag/v1.15.0) | Introduced [JavaScript Engine](/guide/regex-engines#javascript-engine) with better portability and bundle size                                        |
| [v1.16.0](https://github.com/shikijs/shiki/releases/tag/v1.16.0) | Support [Synchronous Usage](/guide/sync-usage)                                                                                                        |
| [v1.19.0](https://github.com/shikijs/shiki/releases/tag/v1.19.0) | Introduced `enableDeprecationWarnings()` function for easier migration. Support object-style `htmlStyle` and new `htmlAttrs` on themed tokens.        |
| [v1.23.0](https://github.com/shikijs/shiki/releases/tag/v1.23.0) | New [`@shikijs/colorized-brackets`](/packages/colorized-brackets) package, thanks to [@MichaelMakesGames](https://github.com/MichaelMakesGames)       |
| [v1.24.0](https://github.com/shikijs/shiki/releases/tag/v1.24.0) | Improved performance and accuracy for the JavaScript engine, thanks to [@slevithan](https://github.com/slevithan)                                     |
| [v1.25.0](https://github.com/shikijs/shiki/releases/tag/v1.25.0) | Separated themes and languages into `@shikijs/themes` and `@shikijs/languages` packages                                                               |
| [v1.26.0](https://github.com/shikijs/shiki/releases/tag/v1.26.0) | Introduced [pre-compiled languages](https://shiki.style/guide/regex-engines#pre-compiled-languages) package for smaller bundle and better performance |
| [v1.27.0](https://github.com/shikijs/shiki/releases/tag/v1.27.0) | New [`shiki-codegen`](/packages/codegen) package for easier fine-grained bundle creation                                                              |
| [v1.29.0](https://github.com/shikijs/shiki/releases/tag/v1.28.0) | Improved the transformer matching algorithm, introduce `matchAlgorithm` option. Thanks to [@fuma-nama](https://github.com/fuma-nama)                  |

Among all these new features, we also includes a lot of new languages support and new themes. Check out [languages](/languages) and [themes](/themes) list for the full list.

Meanwhile, huge thanks to [@slevithan](https://github.com/slevithan)'s great work on [`oniguruma-to-es`](https://github.com/slevithan/oniguruma-to-es) that make the JavaScript engine [supports 97.2% of all the languages](/references/engine-js-compat).

## Breaking Changes

There are **NO** hard breaking changes in v2.0.0. It serves as a stepping stone for the upcoming v3.0.0.

The only change in v2 is that Shiki will now **emit warnings when you use deprecated APIs** that are planned to be removed in v3. Since this might affect end users, we have made a major version bump so you can opt-in to the warnings and prepare for the future removal.

- `v1.x`: Deprecated APIs are still supported, marked on type level only. With optional runtime warnings to opt-in.
- 👉 `v2.0`: No breaking changes, but enable runtime deprecated warnings by default.
- `v3.0`: Remove deprecated APIs, breaking changes.

Expect v3.0.0 to be released soon after v2.0.0.

## Automated Migration

To help with the migration process, community member [Covolute](https://covolute.com/registry/shiki/v1-to-v2) provides an automated codemod that handles most of the API changes from v1 to v2. You can run it directly with:

```bash
npx covolute@latest shiki/v1-to-v2
```

## Deprecations

We highly recommend you to migrate the depreactions as soon as possible, with the warning messages as a guide.

### `getHighlighter` -> `createHighlighter`

There is no functional changes, but more like correcting the naming to avoid confusion. It should be a straightforward find-and-replace.

### WASM Related APIs

Since the introduction of the [engine system](/guide/regex-engines) in v1.16, WebAssembly-related dependencies are no longer a hard requirement. To facilitate tree-shaking and decouple the engines from the core, two packages have been extracted: `@shikijs/engine-oniguruma` and `@shikijs/engine-javascript`. These are also re-exported from the main package as `shiki/engine/oniguruma` and `shiki/engine/javascript`, respectively.

You might need to change your import path:

```ts
import { loadWasm } from 'shiki' // [!code --]
import { loadWasm } from 'shiki/engine/oniguruma' // [!code ++]
```

`loadWasm` field in `createHighlighterCore` is replaced with `engine` field:

```ts
import { createHighlighter } from 'shiki'
import { createOnigurumaEngine } from 'shiki/engine/oniguruma' // [!code ++]

const shiki = await createHighlighter({
  // ...
  loadWasm: () => import('shiki/wasm'), // [!code --]
  engine: createOnigurumaEngine(() => import('shiki/wasm')), // [!code ++]
})
```

### Shiki Compat

The `@shikijs/compat` package that built for compatibility with v0.14 is now deprecated. Please migrate to the main package. This package will be removed in v3.0.

### Transformers Matching Algorithm

The `matchAlgorithm` option for transformers was introduced in v1.29.0, allowing users to choose the matching algorithm. The default value will change from `v1` to `v3` in v3.0.0. We recommend explicitly setting the `matchAlgorithm` option to avoid future breaking changes.

[Learn more](/packages/transformers#matching-algorithm).

### Other Deprecations

- `createdBundledHighlighter` requires a single object-style argument
- `@shikijs/core`
  - The regex engines `createJavaScriptRegexEngine` `createOnigurumaEngine` are no longer included, import them from `@shikijs/engine-oniguruma` and `@shikijs/engine-javascript` respectively
  - `createHighlighterCore` now explicits requires an `engine` field to be passed
  - `loadWasm` field in `createHighlighterCore` is replaced with `engine` field
  - `@shikijs/core/wasm-inline` is replaced with `@shikijs/engine-oniguruma/wasm-inline`
  - Import `FontStyle` and `StackElementMetadata` from `@shikijs/vscode-textmate` instead of `@shikijs/core`

## Tweaking Warnings

If you prefer hard errors instead of warnings, you can run the following code before using Shiki, the first argument decides if warnings should be enabled, the second argument decides if warnings should be thrown as errors:

```ts
import { enableDeprecationWarnings } from 'shiki/core'

enableDeprecationWarnings(true, true) // enable warnings and throw errors

// use crateHighlighter(...) etc. after that
```

### Disable Warnings

If you want to disable the warnings:

```ts
import { enableDeprecationWarnings } from 'shiki/core'

enableDeprecationWarnings(false)
```

## Try It as a User

If you are using Shiki indirectly via other packages, like [`vitepress`](https://vitepress.dev/) or [`@nuxt/content`](https://content.nuxt.com/), where you don't directly control the version of Shiki, you can try adding the following lines to your `package.json` to force the usage of Shiki v2.0.0. This will help you check if the frameworks/tools you are using rely on deprecated Shiki APIs. If they do, please report it to their repositories to raise awareness of the upcoming changes. Thanks!

```json
{
  "resolutions": {
    "shiki": "^2",
    "@shikijs/core": "^2",
    "@shikijs/transformers": "^2",
    "@shikijs/markdown-it": "^2",
    "@shikijs/rehype": "^2"
  }
}
```

## Feedback

Any feedbacks are welcome! Feel free to open an issue on [GitHub](https://github.com/shikijs/shiki) and let us know your thoughts.

---

### Blog V3 (`docs/blog/v3.md`)

---
outline: deep
---

# Shiki v3.0.0

Shiki v3.0.0 is [another](./v2) **boring** release that removes deprecated APIs that have been marked since v1.x and explicitly warned in v2.x.

In Shiki, new features are shipped in minor releases progressively, while the major releases is for cleaning up and removing deprecated APIs.

If you are still on v1.x, please [migrate to v2.x](./v2) first. It should provides you a smooth transition path to v3.0.0.

## Breaking Changes

- `v1.x`: Deprecated APIs are still supported, marked on type level only. With optional runtime warnings to opt-in.
- `v2.0`: No breaking changes, but enable runtime deprecated warnings by default.
- 👉 `v3.0`: Remove deprecated APIs, breaking changes.

### `getHighlighter` -> `createHighlighter`

There is no functional changes, but more like correcting the naming to avoid confusion. It should be a straightforward find-and-replace.

### WASM Related APIs

Since the introduction of the [engine system](/guide/regex-engines) in v1.16, WebAssembly-related dependencies are no longer a hard requirement. To facilitate tree-shaking and decouple the engines from the core, two packages have been extracted: `@shikijs/engine-oniguruma` and `@shikijs/engine-javascript`. These are also re-exported from the main package as `shiki/engine/oniguruma` and `shiki/engine/javascript`, respectively.

You might need to change your import path:

```ts
import { loadWasm } from 'shiki' // [!code --]
import { loadWasm } from 'shiki/engine/oniguruma' // [!code ++]
```

`loadWasm` field in `createHighlighterCore` is replaced with `engine` field:

```ts
import { createHighlighter } from 'shiki'
import { createOnigurumaEngine } from 'shiki/engine/oniguruma' // [!code ++]

const shiki = await createHighlighter({
  // ...
  loadWasm: () => import('shiki/wasm'), // [!code --]
  engine: createOnigurumaEngine(() => import('shiki/wasm')), // [!code ++]
})
```

Meanwhile, `@shikijs/core` package no longer includes the engines or wasm binaries. You need to import them from the `@shikijs/engine-oniguruma` and `@shikijs/engine-javascript` packages.

### Shiki Compat

The `@shikijs/compat` package that built for compatibility with v0.14 is now discontinued. Please migrate to the main package.

### Transformers Matching Algorithm

The `matchAlgorithm` option for transformers was introduced in v1.29.0, allowing users to choose the matching algorithm. The default value is change from `v1` to `v3` in v3.0.0.

[Learn more](/packages/transformers#matching-algorithm).

### Other Breaking Changes

- `createdBundledHighlighter` requires a single object-style argument
- `@shikijs/core`
  - The regex engines `createJavaScriptRegexEngine` `createOnigurumaEngine` are no longer included, import them from `@shikijs/engine-oniguruma` and `@shikijs/engine-javascript` respectively
  - `@shikijs/core/wasm-inline` is replaced with `@shikijs/engine-oniguruma/wasm-inline`
  - Import `FontStyle` and `StackElementMetadata` from `@shikijs/vscode-textmate` instead of `@shikijs/core`

## Try It as a User

If you are using Shiki indirectly via other packages, like [`vitepress`](https://vitepress.dev/) or [`@nuxt/content`](https://content.nuxt.com/), where you don't directly control the version of Shiki, you can try adding the following lines to your `package.json` to force the usage of [Shiki v2.0.0](./v2) first.

```json
{
  "resolutions": {
    "shiki": "^2",
    "@shikijs/core": "^2",
    "@shikijs/types": "^2",
    "@shikijs/transformers": "^2",
    "@shikijs/markdown-it": "^2",
    "@shikijs/rehype": "^2"
  }
}
```

Then run the tool to see if it emits any warnings. If they do, please report it to their repositories to raise awareness of the upcoming changes. And if not, the tool should work as expected with Shiki v3.0.0, where you can then further migrate to v3 as:

```json
{
  "resolutions": {
    "shiki": "^3",
    "@shikijs/core": "^3",
    "@shikijs/types": "^3",
    "@shikijs/transformers": "^3",
    "@shikijs/markdown-it": "^3",
    "@shikijs/rehype": "^3"
  }
}
```

## Feedback

Any feedbacks are welcome! Feel free to open an issue on [GitHub](https://github.com/shikijs/shiki) and let us know your thoughts.

---

### Blog V4 (`docs/blog/v4.md`)

---
outline: deep
---

# Shiki v4.0.0

Shiki v4.0.0 drops support for Node.js 18 and remove deprecated APIs.

In Shiki, new features are shipped in minor releases progressively, while the major releases are for cleaning up and removing deprecated APIs.

## Breaking Changes

### Node.js 18 Dropped

Shiki v4 requires **Node.js ≥ 20**. Node.js 18 reached End-of-Life in April 2025 and is no longer supported.

If you are running Node.js 18, please upgrade to Node.js 20 LTS or later before upgrading to Shiki v4.

### Deprecated APIs Removed

#### `CreatedBundledHighlighterOptions` Removed

The `CreatedBundledHighlighterOptions` interface (a typo, with "Created" instead of "Create") has been removed. Use `CreateBundledHighlighterOptions` instead:

```ts
import type { CreatedBundledHighlighterOptions } from 'shiki' // [!code --]
import type { CreateBundledHighlighterOptions } from 'shiki' // [!code ++]
```

#### `createdBundledHighlighter` Removed

The `createdBundledHighlighter` function (a typo, with "created" instead of "create") has been removed. Use `createBundledHighlighter` instead:

```ts
import { createdBundledHighlighter } from 'shiki' // [!code --]
import { createBundledHighlighter } from 'shiki' // [!code ++]
```

#### `theme` Option Removed in `TwoslashFloatingVue`

The singular `theme` option passed to `TwoslashFloatingVue` (from `@shikijs/vitepress-twoslash`) has been removed. Use `themes` instead:

```ts
app.use(TwoslashFloatingVue, {
  theme: { /* ... */ }, // [!code --]
  themes: { /* ... */ }, // [!code ++]
})
```

#### CSS Class `twoslash-query-presisted` Removed

The misspelled CSS class `twoslash-query-presisted` (a typo, with "**pre**sisted" instead of "**per**sisted") has been removed. Use the correct `twoslash-query-persisted` instead.

---

### Guide Best Performance (`docs/guide/best-performance.md`)

---
outline: deep
---

# Best Performance Practices

This guide will help you to improve the performance of your Shiki usage.

## Cache the Highlighter Instance

The highlighter instance is expensive to create. Most of the time, you should create the highlighter instance once and reuse it for multiple highlight operations (singleton pattern).

For example:

```ts
import { createHighlighterCore } from 'shiki/core'

const highlighterPromise = createHighlighterCore({ /* ... */ })

export async function highlightCode(code: string, lang: string) {
  const highlighter = await highlighterPromise
  return highlighter.codeToHtml(code, lang)
}
```

When you no longer need a highlighter instance, you can call the `dispose()` method to release the resources. (It can't be GC-ed automatically, you need to do it explicitly.)

```ts
highlighter.dispose()
```

## Fine-Grained Bundle

The pre-built bundles are for easy usage, and mostly intended for a Node.js environment where you aren't worried about the bundle size. If you are building a web application or in a resource-constrained environment, it's always better to use the fine-grained bundles to reduce the bundle size and memory usage.

**Avoid importing `shiki`, `shiki/bundle/full`, `shiki/bundle/web` directly**.

Instead, import fine-grained modules like `shiki/core`, `shiki/engine/javascript`, `@shikijs/langs/typescript`, `@shikijs/themes/dark-plus`, etc.

```ts
import { createHighlighterCore } from 'shiki/core'
import { createJavaScriptRegexEngine } from 'shiki/engine/javascript'

const highlighter = await createHighlighterCore({
  themes: [
    import('@shikijs/themes/nord'),
    import('@shikijs/themes/dark-plus'),
    // ...
  ],
  langs: [
    import('@shikijs/langs/typescript'),
    import('@shikijs/langs/javascript'),
    // ...
  ],
  engine: createJavaScriptRegexEngine()
})
```

To compose the fine-grained bundles easily, we also provide the [`shiki-codegen`](/packages/codegen) tool to generate fine-grained bundles for you.

Learn more about [Fine-Grained Bundles](/guide/bundles#fine-grained-bundle).

## Use Shorthands

`createHighlighter` and `createHighlighterCore` load all the themes and languages **upfront** to ensure subsequent highlight operations are synchronous. This can add significant overhead to startup time, especially when you have a lot of themes and languages. Shorthands abstract the theme and language loading process and maintain an internal highlighter instance underneath, only loading the necessary themes and languages when needed. When your highlighting process can be asynchronous, you can use shorthands to reduce startup time.

```ts
import { codeToHtml } from 'shiki'

// Only `javascript` and `nord` will be loaded when calling `codeToHtml`
const html = await codeToHtml('const a = 1', {
  lang: 'javascript',
  theme: 'nord'
})
```

You can also create your own shorthands with fine-grained bundles. Check out the [Create Shorthands with Fine-Grained Bundles](/guide/shorthands#create-shorthands-with-fine-grained-bundles) section for more details.

## JavaScript Engine and Pre-compiled Languages

Shiki provides [two engines](/guide/regex-engines) for executing regular expressions: [`JavaScript`](/guide/regex-engines#javascript-regexp-engine) and [`Oniguruma`](/guide/regex-engines#oniguruma-engine). The Oniguruma engine is WebAssembly-based and compiled from C code, and `JavaScript` is a pure JavaScript engine that translates Oniguruma-flavored regexes to JavaScript regexes.

If you are bundling Shiki for the web, using the JavaScript engine results in a smaller bundle size and faster startup time. The [precompiled languages](/guide/regex-engines#pre-compiled-languages) can further reduce bundle size and startup time, if your target browsers support the latest RegExp features.

Check the [RegExp Engines](/guide/regex-engines) guide for more details.

## Use Workers

Shiki highlights code using regular expressions, which can be CPU-intensive. You can offload the highlighting work to a Web Worker/Node Worker to avoid blocking the main thread.

::: info

🚧 We are still working on a guide for creating workers easily.

:::

---

### Guide Bundles (`docs/guide/bundles.md`)

---
outline: deep
---

# Bundles

The main `shiki` entries bundles all supported themes and languages via lazy dynamic imports. The efficiency shouldn't be a concern to most of the scenarios as the grammar would only be imported/downloaded when it is used. However, when you bundle Shiki into browsers runtime or web workers, even those files are not imported, they still add up to your dist size. We provide the [fine-grained bundle](#fine-grained-bundle) to help you compose languages and themes one-by-one as you need.

::: info
If you are building a web application, or in a performance-sensitive environment, it's always better to use the [fine-grained bundles](#fine-grained-bundle) to reduce the bundle size and memory usage. Learn more about [Best Performance Practices](/guide/best-performance).
:::

## Bundle Presets

To make it easier, we also provide some pre-composed bundles for you to use:

### `shiki/bundle/full`

> [Bundle Size](/guide/#bundle-size): 6.4 MB (minified), 1.2 MB (gzip), async chunks included

The full bundle includes all themes and languages, same as the main `shiki` entry.

### `shiki/bundle/web`

> [Bundle Size](/guide/#bundle-size): 3.8 MB (minified), 695 KB (gzip), async chunks included

The bundle includes all themes and common web languages like (HTML, CSS, JS, TS, JSON, Markdown, etc.) and some web frameworks (Vue, JSX, Svelte, etc.).

Use as normal, all functions from `shiki` are also available in the bundle:

```
/* Detailed source-code truncated for AI context efficiency. */
```

## Fine-grained Bundle

When importing `shiki`, all the themes and languages are bundled as async chunks. Normally it won't be a concern to you as they are not being loaded if you don't use them. In some cases, if you want to control what to bundle, you can use the core and compose your own bundle.

```
/* Detailed source-code truncated for AI context efficiency. */
```

::: info
[Shorthands](/guide/install#shorthands) are only available in bundle presets. For a fine-grained bundle, you can create your own shorthands using [`createSingletonShorthands`](https://github.com/shikijs/shiki/blob/main/packages/core/src/constructors/bundle-factory.ts#L203) or port it yourself.
:::

---

### Guide Compat (`docs/guide/compat.md`)

---
outline: deep
---

# Compatibility Build

::: warning
This is a compatibility build for v0.x users. And this package is no longer maintained since v3. Please migrate to the latest version.
:::

To make deep migration easier, we provide a compatibility build that shimmed the breaking changes from v0.x. You can use it as a drop-in replacement for `shiki` and migrate step by step.

## Installations

<Badges name="@shikijs/compat" />

Set the alias to `shiki` in your `package.json`:



```json
{
  "dependencies": {
    "shiki": "0.14.3", // [!code --]
    "shiki": "npm:@shikijs/compat@1.0" // [!code ++]
  }
}
```

Check the [breaking changes list](/guide/migrate#migrate-from-v0-14) to see if you need to migrate anything manually.

---

### Guide Decorations (`docs/guide/decorations.md`)

# Decorations

We provide a decorations API allowing you to wrap custom classes and attributes around ranges of your code.

```
/* Detailed source-code truncated for AI context efficiency. */
```

The result will be (styled with CSS in this example):

```ts
// @decorations:[{"start":{"line":1,"character":0},"end":{"line":1,"character":11},"properties":{"class":"highlighted-word"}}]
const x = 10
console.log(x)
```

The positions can also be 0-indexed offsets relative to the code:

```
/* Detailed source-code truncated for AI context efficiency. */
```

It renders:

```ts
// @decorations:[{"start":21,"end":24,"properties":{"class":"highlighted-word"}}]
const x = 10
console.log(x)
```

Negative character positions denote characters from the end of a line, starting with the line end:

```ts twoslash
// @twoslash-cache: {"v":1,"hash":"f445be6013dcc1515a5189fe0a44669ee748465f55a6563c00cc29cd14e82b74","data":"N4Igdg9gJgpgziAXAbVAFwJ4AcZJACwgDcYAnEAGhDRgA808BjCMONAAgEsaBbRdgCIxmpAIZpOLAJK9KINqNINEANioAbGGADmafEgAsVNIu0xlIbjB5z1nMLkQAGKo3yLRjGuVUBfCujYjgTEZHI09HgAFKJ2onAAlFxg3gBmnjCCwhBiEtK8ADpgnDxYORxCIuKSYDLWcgpKSACMAEwaWrr6iM1G1KbmeJU51fn1GvaOLiBuHl5hPa3+gTh4hCTkxnQWUVikEDhKGEnDuTV1PAB0jWj8APKpqXDmd6QAChBw3DVyUBCMCEQIAAyiYlOwII9nhwcuwyl88mAIal2HpMrAqojLg0wcoAMx4jo6PRIACsxgGFhutkmLVc7jE8x8SwC1CCa1Cm2o22iewOZEwSQ+CJqlzsDn4YAArjwAEZhKjUxAGdogTTE7p9MFmCzi3ATBx0mYMzzeFrNZZs1ZA9YK7mRIG7faHQXsYXfFiXWaM7ySmXyrlK0nNIldJAATgppB1TBNTJphp69LmZsQrSclsw1pCG3CPMdfJdxyymPOvEuWig9yhL3enw9YF+/0BIAAomAoMinuYIaQ4fXEcjUfh0dkzp6cYplCpI2rOiTEIT+tHBkDKwnHKrvaaFiyVsFbVyIjtCwLi+6sXq/XK7UqAOxqOcawxRmNAvUbpBbuOp8OZ9k2pyeYOiATr8kcQoDqK25MteAaTk0iAABzkk+YaILO2qrsaKZ2h+abJj6CzNEh/7ZoewEns6Z4nGOoy1OWp5KJw8AAPz8G81HMfA7AAD7sFKHYwKkkxQE2AJ4Jx4ESDxkLDpkMCaDwWgcGgEDsIwpAwOIMDYoquItE4fTquhzTTFhFhMTJCAGo4S4wame5WgeQFbCBYFFkkjDqPEcD8GwpD2NoCHKM0JGhguqEWUwPlwDZaq0ouhE7j4zQqL4AC6rjQMEzCsBwVh8CWIyIhc7AALzsMARTsOwNz8MA7BXuwLgaT+ZD8E47D+DV7CVg1TWTJ1FBtbhpD8AAtM03UUL1VksX5VUabFi0AOT4Jw2j4HYW00FAE0AO45FAq3dUUvhyMpJhIKA2xaF8LB4GgCC+L4QA="}
import { DecorationItem } from 'shiki'
// ---cut---
const item: DecorationItem = {
  start: { line: 0, character: 0 },
  end: { line: 0, character: -1 },
  properties: { class: 'highlighted-word' }
}
```

This highlights the entire first line:

```ts
// @decorations:[{"start":{"line":0,"character":0},"end":{"line":0,"character":-1},"properties":{"class":"highlighted-word"}}]
const x = 10
console.log(x)
```

## Use Decorations in Transformers

For advanced use cases, you can use the [Transformers API](./transformers.md) to have full access to the tokens and the HAST tree.

Meanwhile, if you want to append decorations within a transformer, you can do that with:

```
/* Detailed source-code truncated for AI context efficiency. */
```

Note that you can only provide decorations in or before the `preprocess` hook. In later hooks, changes to the decorations array will be ignored.

---

### Guide Dual Themes (`docs/guide/dual-themes.md`)

---
outline: deep
---

# Light/Dark Dual Themes

Shiki supports outputting light/dark dual or multiple themes. Shiki's dual themes approach uses CSS variables to store the colors on each token.

Change the `theme` option in `codeToHtml` to `themes` with `light` and `dark` keys to generate two themes.

```
/* Detailed source-code truncated for AI context efficiency. */
```

The following HTML will be generated ([demo preview](https://htmlpreview.github.io/?https://raw.githubusercontent.com/shikijs/shiki/main/packages/shiki/test/out/dual-themes.html)):

```html
<pre
  class="shiki shiki-themes min-light nord"
  style="background-color:#ffffff;--shiki-dark-bg:#2e3440ff;color:#24292eff;--shiki-dark:#d8dee9ff"
  tabindex="0"
>
  <code>
    <span class="line">
      <span style="color:#1976D2;--shiki-dark:#D8DEE9">console</span>
      <span style="color:#6F42C1;--shiki-dark:#ECEFF4">.</span>
      <span style="color:#6F42C1;--shiki-dark:#88C0D0">log</span>
      <span style="color:#24292EFF;--shiki-dark:#D8DEE9FF">(</span>
      <span style="color:#22863A;--shiki-dark:#ECEFF4">"</span>
      <span style="color:#22863A;--shiki-dark:#A3BE8C">hello</span>
      <span style="color:#22863A;--shiki-dark:#ECEFF4">"</span>
      <span style="color:#24292EFF;--shiki-dark:#D8DEE9FF">)</span>
      </span>
    </code>
</pre>
```

To make it reactive to your site's theme, you need to add a short CSS snippet:

## Query-based Dark Mode

```css
@media (prefers-color-scheme: dark) {
  .shiki,
  .shiki span {
    color: var(--shiki-dark) !important;
    background-color: var(--shiki-dark-bg) !important;
    /* Optional, if you also want font styles */
    font-style: var(--shiki-dark-font-style) !important;
    font-weight: var(--shiki-dark-font-weight) !important;
    text-decoration: var(--shiki-dark-text-decoration) !important;
  }
}
```

## Class-based Dark Mode

```css
html.dark .shiki,
html.dark .shiki span {
  color: var(--shiki-dark) !important;
  background-color: var(--shiki-dark-bg) !important;
  /* Optional, if you also want font styles */
  font-style: var(--shiki-dark-font-style) !important;
  font-weight: var(--shiki-dark-font-weight) !important;
  text-decoration: var(--shiki-dark-text-decoration) !important;
}
```

## Multiple Themes

It's also possible to support more than two themes. In the `themes` object, you can have an arbitrary number of themes, and specify the default theme with `defaultColor` option.

```
/* Detailed source-code truncated for AI context efficiency. */
```

`span` tokens would be generated with respective theme's css variables:

```html
<span style="color:#1976D2;--shiki-dark:#D8DEE9;--shiki-dim:#566575">console</span>
```

After that, you need to apply theme's css variables on element with `shiki` class and tokens under it, for example, based on parent's `data-theme` property:

```css
[data-theme='dark'] .shiki,
[data-theme='dark'] .shiki span {
  background-color: var(--s-dark-bg) !important;
  color: var(--s-dark) !important;
}

[data-theme='dim'] .shiki,
[data-theme='dim'] .shiki span {
  background-color: var(--s-dim-bg) !important;
  color: var(--s-dim) !important;
}
```

[Demo preview](https://htmlpreview.github.io/?https://raw.githubusercontent.com/shikijs/shiki/main/packages/shiki/test/out/multiple-themes.html)

### Without Default Color

If you want to take full control of the colors or avoid using `!important` to override, you can optionally disable the default color by setting `defaultColor` to `false`.

```
/* Detailed source-code truncated for AI context efficiency. */
```

With it, a token would be generated like:

```html
<span style="--shiki-dark:#D8DEE9;--shiki-light:#2E3440">console</span>
```

In that case, the generated HTML would have no style out of the box, you need to add your own CSS to control the colors.

It's also possible to control the theme in CSS variables. For more, refer to the great research and examples by [@mayank99](https://github.com/mayank99) in [this issue #6](https://github.com/antfu/shikiji/issues/6).

## `light-dark()` Function

You can also use [`light-dark()`](https://developer.mozilla.org/en-US/docs/Web/CSS/color_value/light-dark) function to avoid manually maintaining the CSS variables.

Set `defaultColor` to a special value `light-dark()` to use it. When using this, it requires both `light` and `dark` themes to be provided.

```
/* Detailed source-code truncated for AI context efficiency. */
```

:::info Compatibility Note
The `light-dark()` function is relatively new and may not be supported in older browsers. [Can I use?](https://caniuse.com/?search=css-light-dark)
:::

---

### Guide Grammar State (`docs/guide/grammar-state.md`)

# Grammar State

`GrammarState` is a special token that holds the grammar context and allows you to highlight from an intermediate grammar state, making it easier to highlight code snippets.

For example, if we want to highlight the type annotation `Pick<MyObject, string>[]`, we can get the grammar state with the `getLastGrammarState` method, and pass into the `grammarState` option:

```ts
import { createHighlighter } from 'shiki'

const shiki = await createHighlighter({ langs: ['ts'], themes: ['github-dark'] })

const stateTypeAnnotation = shiki.getLastGrammarState('let a:', { lang: 'ts', theme: 'github-dark' })

const highlightedType = shiki.codeToHtml(
  'Pick<MyObject, string>[]',
  {
    lang: 'ts',
    theme: 'github-dark',
    grammarState: stateTypeAnnotation // <--- this
  }
)
```

Now Shiki would highlight correctly as it knows to start as the type annotation. You can keep that grammar state object for multiple uses as well.

<img width="223" alt="image" src="https://github.com/shikijs/shiki/assets/11247099/c896c2ae-2a88-428b-9d06-2d2552eaae8b">

## Grammar Context Code

For one-off grammar context shifting, we also provide a shorthand by the `grammarContextCode` option:

```ts
const highlightedType = shiki.codeToHtml(
  'Pick<MyObject, string>[]',
  {
    lang: 'ts',
    theme: 'github-dark',
    grammarContextCode: 'let a:' // same as above, a temporary grammar state is created internally
  }
)
```

### Get Grammar State from HAST

The `getLastGrammarState` method runs the highlighting process internally and returns the grammar state. It might result in executing highlighting twice if you are working on something like pausable highlighting. In that case, you can pass the highlighted `hast` node to the `getLastGrammarState` to get the grammar state we stored in an internal WeakMap:

```ts
const shiki = await getHighlighter(/* ... */)

const root = shiki.codeToHast(/* ... */)

const grammarState = shiki.getLastGrammarState(root) // pass the hast root instead of code
```

---

### Guide Install (`docs/guide/install.md`)

---
outline: deep
---

# Installation & Usage

<Badges name="shiki" />

## Installation

Install via npm, or see [CDN Usage](#cdn-usage):
::: code-group

```sh [npm]
npm install -D shiki
```

```sh [yarn]
yarn add -D shiki
```

```sh [pnpm]
pnpm add -D shiki
```

```sh [bun]
bun add -D shiki
```

```sh [deno]
deno add npm:shiki
```

:::

## Integrations

We also provide some integrations:

- [markdown-it Plugin](/packages/markdown-it)
- [Rehype Plugin](/packages/rehype)
- [TypeScript Twoslash Integration](/packages/twoslash)
- [Monaco Editor Syntax Highlight](/packages/monaco)
- [CLI](/packages/cli)
- [Common Transformers](/packages/transformers)

## Usage

### Shorthands

The quickest way to get started with `shiki` is to use the provided shorthand functions. These will load the necessary themes and languages on demand, and automatically cache them in memory.

Passing your code snippet to the `codeToHtml` function with the `lang` and `theme` specified, it will return a highlighted HTML string that you can embed in your page. The generated HTML contains inline style for each token, so you don't need extra CSS to style it.

```
/* Detailed source-code truncated for AI context efficiency. */
```

Going a bit advanced, you can also use `codeToTokens` or `codeToHast` to get the intermediate data structure, and render them by yourself:

```ts twoslash theme:min-dark
// @twoslash-cache: {"v":1,"hash":"b815da6ae7b98fc8742ecce6129ff09de56b53a6198bbf341d9bfeceb03a3cda","data":"N4Igdg9gJgpgziAXAbVAFwJ4AcZJACwgDcYAnEAGhDRgA808AKAQwBsBLZuASgAIBjCGDhoB0GABUIUgNYxhiXo0GxFI0uzABzCrwhY07IXEUBhcVNny4AeQNHhAHgBCAVzBRWMKABlm212YtGF03Dy8oCXwYAFsYAD4+AF543gAFUggY9jgYRythACV4V1Y0eIAdMHYYrAhSURVJaQg5YUoQEWYGpABOKi9tNHwkAEYAJio0buCGRBAmy1brDo4wXEQABip+fG7mfhpyRF6AXwp0bA2CYjIOmno8QWFRNGWFXijY7wLkAF1/h0oBB+Ah5uMACK8bqkZgYPQAM14bzacF0CPYpBEvCgNWsDl4OV4a3gulyzygOLxwgJRJR1kJYGhxM0MAAdB0uj1EAAWABsA3kWmGSAF1BmMDm1HeCAGrKQkwWe1hhzuiAAHOdLjg8IQSOQpnQpSwOFw+IsWqjlOI1GgNNpdPpDMYzBZLdY7M6nGFPN4/AEgiFeD6Il84olFBksjk8gU4MU4KVylUanUGmJYEtUZzpty+QBmQVDEaICZTCVSi1x1byxCK3b7VXHcYAVi11CuutuBuoRqYWEyOAaGD4rH8WgA/IoQ37x4FgrwAD68ADKOH4nFY/q085gS947lgGPWUBz3Tmo1G2xAg2FJZ55dIszwY+0NfWSELSsbRwV7cwOrzHqdyGo88yMAO+hkJgfDmLAnoOHAK6aFoXhhnkM6RNE4ZssM3yKOhxRaDkdrMF6ACCYDwsuK52ihPjsEcbAAKrVEILiHqG2FBuoKHxGe3ITGKt4iogLaPs+8x4XE74bA+34qr+danH8OziHgqb1KIwAZs0ca8KcvAIpkMS8AA5HA+DsDI7BmVUVTPNiOn0sIBm8Ek0IAO7MIxulZtYjBmY4uJEAIY5wHASQVCACIQBA0XxAARt0jgAPQhfEZm6MAVS8MS46KGZ+BoDErBZblyLcYV2RgAAtFA3QyHZYCnNwHRxNMSCgEa+JCHgaAIKcpxAA="}
import { codeToTokens } from 'shiki'

const { tokens } = await codeToTokens('<div class="foo">bar</div>', {
  lang: 'html',
  theme: 'min-dark'
})
```

```ts twoslash theme:catppuccin-mocha
// @twoslash-cache: {"v":1,"hash":"c2d38cc11de83aa2f1d11651d47d604c307a1a81460c52619a8483484bbcdeb6","data":"N4Igdg9gJgpgziAXAbVAFwJ4AcZJACwgDcYAnEAGhDRgA808AKAQwBsBLZuASgAIBjCGDhoB0GABUIACS5pEvRoNgKRpdmADmFXhCxp2QuAoDC4qbJEB5fYeEAeAEIBXMFFYwoAGWZbnzTRgdFzcPKAl8GABbGAA+PgBeWN4ABVIIKPY4GHsAJQgINFiAHTB2KKwIUlFlSRk5ShARZmqkAE4qDy00fCQARgAGKjQWwIZEEFqLBs6NXEQhyfwW5n4ackQ2gF8KdGx5gmIyRpp6PEFhUWWRBXzCxubWxAAWACZOmG7el+HRmHGCDMQBwwPN3ksVmtjogAGw7PY4PCEEjkYZ0AEsDhcPhTeoiJTiVRodRaHR6AxGUzmPFoGwUhwhdyeHx+AJBXiMsIRaJxbgKNIZLI5O5FUrlSrVMSwaYiB4jJ4AVgVHy+/UW8rG52plgYs1BSHB/GWpFW6362121H2SKOqOo6KYWHSOGqGD4rF8mgUxUmcDgPrlLXGbQ6wM+mh6SGev1ImomHq0jRB8wAzFQjZCzYhXvCrYiJsjjmizhNGE69GRMHwzLA6XY4ABlDSaDzcmJOVxM8KRGKxAB0PR5CjbMFyME0WWJzHpAEEwBheAAfXgN4nNrzsdZsACqZSEHdCnhHOjUzdigaefT60bDqsQyuofwBg5iSbmUfTxtN0JzAF10+IeDilUojAFKdQ6rwWy8AAZukUS8AA5HA+DsAA1uwiGlKUFwiLw1yiAkvDMAA7swm7gTKaCMIhA7ogAtKQni8GBgisFUChMVAADcUGITowClLwvAJl6SH8H6/FCbwL4wAoiH8NOWBYM4/D8Bo9FRBAGZYWAWzcI0MQjEgoDop8cB2HgaAIFsWxAA=="}
import { codeToHast } from 'shiki'

const hast = await codeToHast('.text-red { color: red; }', {
  lang: 'css',
  theme: 'catppuccin-mocha'
})
```

### Highlighter Usage

The [shorthands](#shorthands) we provided are executed asynchronously as we use WASM and load themes and languages on demand internally. In some cases, you may need to highlight code synchronously, so we provide the `createHighlighter` function to create a highlighter instance that can later be used synchronously.

The usage is pretty much the same as with `codeToHtml`, where each theme and language file is a dynamically imported ES module. It would be better to list the languages and themes **explicitly** to have the best performance.

```
/* Detailed source-code truncated for AI context efficiency. */
```

:::info Important Note
Highlighter instance should be **long-lived singleton**. You might need to cache it somewhere and reuse it across your application. Avoid calling `createHighlighter` in hot functions or loops.

If running on Node.js, we recommend using the [Shorthands](#shorthands) which manages the highlighter instance and dynamic theme/language loading for you.
:::

Additionally, if you want to load themes and languages after the highlighter is created, you can use the `loadTheme` and `loadLanguage` methods.

```ts twoslash
// @twoslash-cache: {"v":1,"hash":"8ce2944f34901319a707c331109f0ab6777c21709e2685cf9e4136e2d0985b06","data":"N4Igdg9gJgpgziAXAbVAFwJ4AcZJACwgDcYAnEAGhDRgA808BjCMONAAnwEsBzfAG174apROwASQwXxEBxGGDJdGAHgBCAVzBR+MKABkAhmB4bDPGBXabtuqABV8MALYwAfJRBtDpBogCsAAxUuiZo+EgAjJFUaD4WfgRSQiKegopRVIz4PoaMqYgAbAC+FOjYuIgExGSeNPR4ABRYpBA4vhgAlOwAytwA1lwAkmAiYIb86lo6ekYmZhZWNjMOTq5uAHT8EIarLjBijRvH4ftwh8t2jvvsAD7s164jWBoc9wA64Cwwn53IALrdAC8bnYAAVWs4uHAYCoiBAuFAPFQoBBGAgqvodlB2IZ2KdXPiIPinJxkjIyFY4MT+IYROwuBxGMZ2AAjGDsDQwnFwDBgbKtSBc/gYDaeby+JCFABMIQUPHCSAAnLF4jBEttdo9cCEuBlEDEQNlcvlagaABylco4PCEEjkWJ0RLMVgcbh8aTCMhiSQelJkeSKUjKKa2WbGUzmSzWaZXNbucVxSWIJX+OVhCIGw1JhK28leh0gdKVWVGnKkPIFEplagVW01Qv1RLNVrtTDdPpcQYjMYTUMrOaRxYxsN7dZbbGDhYHdhHY60kznWeXcPzKN3dhTqPPV4bno4RhcCZbix/QHsEHgyHQ2HwxHIkCo9F4LG7XHsBdDjloYmnMl+ilSCpGk6TIBkmRZdlOW5dheX5fBBQgYVRUTHw/EiQIAHZ0wVTNIlLHN1TwTUDAjac0j1EssnLSszUiS1/iyaBKhAAB6ViP2xEkzlxbQPzIqM4FxAAzelGFIGA6S4Fh3nGAB3QxGX/AR/VICctXjRoAHIiEZeAYQAWk9NAtM6WTDAUpT3RUwD1NItcLG09E4FMzxXDiJBQCdBQ4GksA8DQBBimKIA=="}
import { createHighlighter } from 'shiki'

const highlighter = await createHighlighter({ themes: [], langs: [] })
// ---cut---
// load themes and languages after creation
await highlighter.loadTheme('vitesse-light')
await highlighter.loadLanguage('css')
```

Since Shiki v1.0, it requires all themes and languages to be loaded explicitly.

```
/* Detailed source-code truncated for AI context efficiency. */
```

If you want to load all themes and languages (not recommended), you can iterate over all keys from `bundledLanguages` and `bundledThemes`.

```
/* Detailed source-code truncated for AI context efficiency. */
```

### Fine-grained Bundle

When importing `shiki`, all the themes and languages are bundled as async chunks. Normally it won't be a concern to you as they are not being loaded if you don't use them. In some cases, if you want to control what to bundle, you can use the core and compose your own bundle.

Check out the [Fine-grained Bundle](/guide/bundles#fine-grained-bundle) section for more details.

### Bundle Presets

We also provide some pre-composed bundles for you to use easily, you can learn more about them in the [bundles section](/guide/bundles).

### CJS Usage

`shiki` is published as ESM-only to reduce the package size. It's still possible to use it in CJS, as Node.js supports importing ESM modules dynamically in CJS.

For example, the following ESM code:

```
/* Detailed source-code truncated for AI context efficiency. */
```

Can be written in CJS as:

```
/* Detailed source-code truncated for AI context efficiency. */
```

### CDN Usage

To use `shiki` in the browser via CDN, you can use [esm.run](https://esm.run) or [esm.sh](https://esm.sh).

```html theme:rose-pine
<body>
  <div id="foo"></div>

  <script type="module">
    // be sure to specify the exact version
    import { codeToHtml } from 'https://esm.sh/shiki@3.0.0'
    // or
    // import { codeToHtml } from 'https://esm.run/shiki@3.0.0'

    const foo = document.getElementById('foo')
    foo.innerHTML = await codeToHtml('console.log("Hi, Shiki on CDN :)")', {
      lang: 'js',
      theme: 'rose-pine'
    })
  </script>
</body>
```

It's quite efficient as it will only load the languages and themes on demand. For the code snippet above, only four requests will be fired (`shiki`, `@shikijs/themes/vitesse-light`, `@shikijs/langs/javascript`, `shiki/wasm.mjs`), with around 200KB data transferred in total.

[Demo](https://jsfiddle.net/t7brz23v/)

### Cloudflare Workers

Cloudflare Workers [does not support initializing WebAssembly from binary data](https://community.cloudflare.com/t/fixed-cloudflare-workers-slow-with-moderate-sized-webassembly-bindings/184668/3), so the default wasm build won't work. You need to upload the wasm as assets and import it directly.

Meanwhile, it's also recommended to use the [Fine-grained Bundle](#fine-grained-bundle) approach to reduce the bundle size.

```
/* Detailed source-code truncated for AI context efficiency. */
```

---

### Guide Load Lang (`docs/guide/load-lang.md`)

# Load Custom Languages

See [All Builtin Languages](/languages) first.

You can load custom languages by passing a TextMate grammar object into the `langs` array.

```
/* Detailed source-code truncated for AI context efficiency. */
```

You can also load languages after the highlighter has been created.

```
/* Detailed source-code truncated for AI context efficiency. */
```

## Migrate from v0.14

Since v1.0, `shiki` now is environment agnostic, we don't have access to the file system. That means the `path` property `shiki@0.14` supports is not available in v1.0, and you must to read the files yourself and pass in the object.

For example, the following would not work:

```ts
const highlighter = await createHighlighter({
  langs: [
    {
      name: 'vue-vine',
      scopeName: 'source.vue-vine',
      // ‼️ This would not work!
      path: join(__dirname, './vine-ts.tmLanguage.json'),
      embeddedLangs: [
        'vue-html',
        'css',
        'scss',
        'sass',
        'less',
        'stylus',
      ],
    },
  ],
  themes: []
})
```

Instead, load that file yourself (via `fs`, `import()`, `fetch()`, etc.):

```ts
const vineGrammar = JSON.parse(fs.readFileSync(join(__dirname, './vine-ts.tmLanguage.json'), 'utf8'))

const highlighter = await createHighlighter({
  langs: [
    {
      name: 'vue-vine',
      scopeName: 'source.vue-vine',
      embeddedLangs: [
        'vue-html',
        'css',
        'scss',
        'sass',
        'less',
        'stylus',
      ],
      ...vineGrammar
    },
  ],
  themes: []
})
```

## Custom Language Aliases

You can register custom language aliases with the `langAlias` option. For example:

```
/* Detailed source-code truncated for AI context efficiency. */
```

---

### Guide Load Theme (`docs/guide/load-theme.md`)

# Load Custom Themes

See [All Builtin Themes](/themes) first.

You can load custom themes by passing a `Theme` object into the `themes` array.

```
/* Detailed source-code truncated for AI context efficiency. */
```

You can also load themes after the highlighter has been created.

```
/* Detailed source-code truncated for AI context efficiency. */
```

The theme is a TextMate theme in JavaScript object. For example, [it should look like this](https://github.com/antfu/textmate-grammars-themes/blob/main/packages/tm-themes/themes/dark-plus.json).

---

### Guide Migrate (`docs/guide/migrate.md`)

---
outline: deep
---

# Migration

We suggest you to migrate step by step, following each version's migration guide.

## Migrate from v3.0

If you are on v3.0, v4.0 only drops support for Node.js 18 and remove deprecated APIs, so you should be able to directly bump to v4.0. Read [Shiki v4.0](/blog/v4) for more details.

## Migrate from v2.0

If you are on v2.0 and there is no warning in your usage, you should be able to directly bump to v3.0, read [Shiki v3.0](/blog/v3) for more details.

## Migrate from v1.0

We recommend you to [migrate to v2.0 first](/blog/v2), then to v3.0.

## Migrate from v0.14

The v1.0 release of Shiki is a major rewrite that we took the chance to revise every design decision we made in the past. We originally had a separate package name as [Shikiji](https://github.com/antfu/shikiji) to experiment with the new design, now it's merged back to Shiki as v1.0.

> [!TIP] Learn more
> Interested in the story behind v1.0? Check out this [blog post](https://nuxt.com/blog/shiki-v1) for more details.

Compare to [`shiki@0.14.3`](https://github.com/shikijs/shiki/releases/tag/v0.14.3), the list of breaking changes are:

### Hard Breaking Changes

Breaking changes that you need to migrate manually:

- CJS and IIFE builds are dropped. See [CJS Usage](/guide/install#cjs-usage) and [CDN Usage](/guide/install#cdn-usage) for more details.
- `codeToHtml` uses [`hast`](https://github.com/syntax-tree/hast) internally. The generated HTML will be a bit different but should behave the same.
- `css-variables` theme is no longer supported. Use the [dual themes](/guide/dual-themes) approach instead, or learn more at the [Theme Colors Manipulation](/guide/theme-colors) page.

### Soft Breaking Changes

Breaking changes applies to main package `shiki`, but are shimmed by the [compatible build `@shikijs/compat`](/guide/compat#compatibility-build):

- Top-level named exports `setCDN`, `loadLanguage`, `loadTheme`, `setWasm` are dropped as they are not needed anymore.
- `BUNDLED_LANGUAGES`, `BUNDLED_THEMES` are moved to `@shikijs/langs` and `@shikijs/themes` and renamed to `bundledLanguages` and `bundledThemes` respectively.
- `theme` option for `createHighlighter` is dropped, use `themes` with an array instead.
- Highlighter does not maintain an internal default theme context. `theme` option is required for `codeToHtml` and `codeToTokens`.
- `codeToThemedTokens` is renamed to `codeToTokensBase`, a higher level `codeToTokens` is added.
- `codeToTokens` sets `includeExplanation` to `false` by default.
- `.ansiToHtml` is merged into `.codeToHtml` as a special language, `ansi`. Use `.codeToHtml(code, { lang: 'ansi' })` instead.
- `lineOptions` is dropped in favor of the fully customizable `transforms` option.
- `LanguageRegistration`'s `grammar` field is flattened to `LanguageRegistration` itself, refer to the types for more details.

### Ecosystem Packages

- `shiki-twoslash` has been completely rewritten. It's no longer a wrapper around Shiki highlighter, but instead, it's now a Shiki transformer that can be plugged in any integrations that supports SHiki transformers. The package is now [`@shikijs/twoslash`](/packages/twoslash).
- Integrations of `shiki-twoslash`, like `gatsby-remark-shiki-twoslash` etc, will be slowly moved to a general Shiki version. Before that, you can use [`@shikijs/rehype`](/packages/rehype) or [`@shikijs/markdown-it`](/packages/markdown-it) to integrate Shiki into those meta-frameworks.
- New official integrations like [`@shikijs/monaco`](/packages/monaco), [`@shikijs/cli`](/packages/cli), [`@shikijs/rehype`](/packages/rehype), [`@shikijs/markdown-it`](/packages/markdown-it) are introduced.
- `shiki-renderer-path` and `shiki-renderer-svg` packages are being deprecated due to low usage. If need them, please open an issue with your use case, we are open to bring them back.
- `vuepress-plugin-shiki` is deprecated as [VuePress](https://github.com/vuejs/vuepress#status) is no longer recommended. Its successor [VitePress](https://vitepress.dev/) has a built-in Shiki integration.

## Migrate from Shikiji

If you are already using [Shikiji](https://github.com/antfu/shikiji), first make sure you are on the latest minor v0.10. Then the migration should be very straightforward by renaming the packages:

- `shikiji` -> `shiki`
- `shikiji-core` -> `@shikijs/core`
- `shikiji-twoslash` -> `@shikijs/twoslash`
- `shikiji-transformers` -> `@shikijs/transformers`
- `shikiji-monaco` -> `@shikijs/monaco`
- `shikiji-cli` -> `@shikijs/cli`
- `markdown-it-shikiji` -> `@shikijs/markdown-it`
- `rehype-shikiji` -> `@shikijs/rehype`

---

### Guide Regex Engines (`docs/guide/regex-engines.md`)

---
outline: deep
---

# RegExp Engines

TextMate grammars are based on regular expressions that match tokens. More specifically, they assume that [Oniguruma](https://github.com/kkos/oniguruma) (a powerful regex engine written in C) will be used to interpret the regular expressions. To make this work in JavaScript, we compile Oniguruma to WebAssembly to run in the browser or Node.js.

Shiki also offers the ability to switch the regex engine or provide a custom implementation. To do so, add an `engine` option to `createHighlighter` or `createHighlighterCore`. For example:

```ts
import { createHighlighter } from 'shiki'

const shiki = await createHighlighter({
  themes: ['nord'],
  langs: ['javascript'],
  engine: { /* custom engine */ }
})
```

Shiki comes with two built-in engines:

## Oniguruma Engine

This is the default engine that uses the compiled Oniguruma WebAssembly.

```ts
import { createHighlighter } from 'shiki'
import { createOnigurumaEngine } from 'shiki/engine/oniguruma'

const shiki = await createHighlighter({
  themes: ['nord'],
  langs: ['javascript'],
  engine: createOnigurumaEngine(import('shiki/wasm'))
})
```

## JavaScript RegExp Engine

This engine uses JavaScript's native `RegExp`. Since regular expressions used by TextMate grammars are written for Oniguruma, we use [Oniguruma-To-ES](https://github.com/slevithan/oniguruma-to-es) to transpile Oniguruma patterns to native JavaScript regexes.

```ts {2,4,9}
import { createHighlighter } from 'shiki'
import { createJavaScriptRegexEngine } from 'shiki/engine/javascript'

const jsEngine = createJavaScriptRegexEngine()

const shiki = await createHighlighter({
  themes: ['nord'],
  langs: ['javascript'],
  engine: jsEngine
})

const html = shiki.codeToHtml('const a = 1', { lang: 'javascript', theme: 'nord' })
```

The advantage of using the JavaScript engine is that it doesn't require loading a large WebAssembly file for Oniguruma. It is also faster for some languages, since the regular expressions run as native JavaScript.

Please check the [compatibility table](/references/engine-js-compat) for the support status of languages you are using. As of Shiki 3.9.1, all built-in languages are supported.

::: info
The JavaScript engine is best when running in the browser and in cases when you want to control the bundle size. If you run Shiki on Node.js (or at build time) and bundle size or WebAssembly support is not a concern, the Oniguruma engine ensures maximum language compatibility.
:::

### Use with Unsupported Languages

Unlike the Oniguruma engine, the JavaScript engine is strict by default. It will throw an error if it encounters an invalid Oniguruma pattern or a pattern that it cannot convert. If you want best-effort results for unsupported grammars, you can enable the `forgiving` option to suppress any conversion errors:

```ts
const jsEngine = createJavaScriptRegexEngine({ forgiving: true })
// ...use the engine
```

This can result in highlighting mismatches, so check your results.

### JavaScript Runtime Target

For best results, the JavaScript engine uses the [RegExp `v` flag](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/unicodeSets), which is available in Node.js v20+ and ES2024 ([browser compatibility](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/unicodeSets#browser_compatibility)). For older environments, it automatically uses the `u` flag instead, but this results in a few less grammars being supported.

By default, the runtime target is automatically detected. You can override this behavior by setting the `target` option:

```ts
const jsEngine = createJavaScriptRegexEngine({
  target: 'ES2018', // or 'auto' (default), 'ES2024', 'ES2025'
})
```

### Pre-compiled Languages

Instead of compiling regular expressions on-the-fly, we also provide pre-compiled languages for the JavaScript engine to further reduce startup time.

::: warning
Pre-compiled languages are not yet supported, due to a [known issue](https://github.com/shikijs/shiki/issues/918) that affects many languages. Please use with caution.
:::

::: info
Pre-compiled languages require support for RegExp UnicodeSets (the `v` flag), which requires ES2024 or Node.js 20+, and may not work in older environments. [Can I use](https://caniuse.com/mdn-javascript_builtins_regexp_unicodesets).
:::

You can install them with `@shikijs/langs-precompiled`, and change your `@shikijs/langs` imports to `@shikijs/langs-precompiled`:

```ts
import { createHighlighterCore } from 'shiki/core'
import { createJavaScriptRawEngine } from 'shiki/engine/javascript'

const highlighter = await createHighlighterCore({
  langs: [
    import('@shikijs/langs/javascript'), // [!code --]
    import('@shikijs/langs/typescript'), // [!code --]
    import('@shikijs/langs-precompiled/javascript'), // [!code ++]
    import('@shikijs/langs-precompiled/typescript'), // [!code ++]
    // ...
  ],
  themes: [
    import('@shikijs/themes/nord'),
  ],
  engine: createJavaScriptRegexEngine(), // [!code --]
  engine: createJavaScriptRawEngine(), // [!code ++]
})
```

If you are not using custom grammars that require transpilation, you can use `createJavaScriptRawEngine` to skip the transpilation step, further reducing bundle size.

If you are using [`shiki-codegen`](/packages/codegen), you can generate pre-compiled languages with the `--precompiled` and `--engine=javascript-raw` flags.

---

### Guide Shorthands (`docs/guide/shorthands.md`)

# Shorthands

The easier way to get started with `shiki` is to use the provided shorthand functions. These will load the necessary themes and languages on demand, and automatically cache them in memory. Different from `createHighlighter` and `createHighlighterCore`, the operations are asynchronous.

```
/* Detailed source-code truncated for AI context efficiency. */
```

## Create Shorthands with Fine-Grained Bundles

You can create your own shorthands with fine-grained bundles. Here is an example of creating shorthands with fine-grained bundles:

```ts
import { createBundledHighlighter, createSingletonShorthands } from 'shiki/core'
import { createJavaScriptRegexEngine } from 'shiki/engine/javascript'

const BundledLanguage = {
  typescript: () => import('@shikijs/langs/typescript'),
  javascript: () => import('@shikijs/langs/javascript'),
  vue: () => import('@shikijs/langs/vue'),
}

const BundledTheme = {
  'light-plus': () => import('@shikijs/themes/light-plus'),
  'dark-plus': () => import('@shikijs/themes/dark-plus'),
}

// This creates your custom 'createHighlighter' function with fine-grained bundles
export const createHighlighter = /* @__PURE__ */ createBundledHighlighter<
  BundledLanguage,
  BundledTheme
>({
  langs: bundledLanguages,
  themes: bundledThemes,
  engine: () => createJavaScriptRegexEngine(),
})

// This creates the shorthands for you
export const {
  codeToHtml,
  codeToHast,
  codeToTokensBase,
  codeToTokens,
  codeToTokensWithThemes,
  getSingletonHighlighter,
  getLastGrammarState,
} = /* @__PURE__ */ createSingletonShorthands(
  createHighlighter,
)
```

You can also use [`shiki-codegen`](/packages/codegen) to generate the fine-grained bundles for you.

---

### Guide Sync Usage (`docs/guide/sync-usage.md`)

# Synchronous Usage

The `await createHighlighter()` and `highlighter.codeToHtml()` are already the effort to do the separations of asynchronism and synchronism. For most of the cases, you should be able to resolve the async part in the initialization phase and use the highlighter synchronously later.

In some extreme cases that you need to run Shiki completely synchronously, since v1.16, we provide a synchronous version of the core API. You can use `createHighlighterCoreSync` to create a highlighter instance synchronously.

```ts
import js from '@shikijs/langs/javascript'
import nord from '@shikijs/themes/nord'
import { createHighlighterCoreSync } from 'shiki/core'
import { createJavaScriptRegexEngine } from 'shiki/engine/javascript'

const shiki = createHighlighterCoreSync({
  themes: [nord],
  langs: [js],
  engine: createJavaScriptRegexEngine()
})

const html = shiki.highlight('console.log(1)', { lang: 'js', theme: 'nord' })
```

When doing so, it requires all `themes` and `langs` to be provide as plain objects. Also an explicit `engine` is required to be provided. With the new [JavaScript RegExp engine](/guide/regex-engines#javascript-regexp-engine) you are able to create an engine instance synchronously as well.

The [Oniguruma Engine](/guide/regex-engines#oniguruma-engine) can only be created asynchronously, so you need to resolve the engine promise before creating the sync highlighter.

```ts
import js from '@shikijs/langs/javascript'
import nord from '@shikijs/themes/nord'
import { createHighlighterCoreSync } from 'shiki/core'
import { createOnigurumaEngine } from 'shiki/engine/oniguruma'

// Load this somewhere beforehand
const engine = await createOnigurumaEngine(import('shiki/wasm'))

const shiki = createHighlighterCoreSync({
  themes: [nord],
  langs: [js],
  engine, // if a resolved engine passed in, the rest can still be synced.
})

const html = shiki.highlight('console.log(1)', { lang: 'js', theme: 'nord' })
```

---

### Guide Theme Colors (`docs/guide/theme-colors.md`)

# Theme Colors Manipulation

## Arbitrary Color Values

Usually, TextMate themes expect the color values of each token to be a valid hex color value. This limitation is inherited from [`vscode-textmate`](https://github.com/microsoft/vscode-textmate). However, in Shiki v0.9.15 we introduced an automatic workaround by replacing non-hex color values with a placeholder and replacing them back on tokenization. This would allows you to use themes with arbitrary color values for rendering without worrying about the technical details:

```
/* Detailed source-code truncated for AI context efficiency. */
```

::: info Notice
Use this carefully as this will diverge from TextMate theme compatibility.

This may make the theme incompatible with non-web usage such as [`shiki-cli`](/packages/cli) and [`shiki-monaco`](/packages/monaco).
:::

Learn more about how to [load themes](./load-theme).

## Color Replacements

You can also use the `colorReplacements` option to replace the color values of the theme. This is useful when you want to use a theme with a different color palette. It can be provided on both the theme object and the `codeToHast` `codeToHtml` options.

The `colorReplacements` object should follow a color-to-color format, where the keys represent the color to be replaced and the value represents the new color:

```js
const html = await codeToHtml(
  code,
  {
    lang: 'js',
    theme: 'min-dark',
    colorReplacements: {
      '#ff79c6': '#189eff'
    }
  }
)
```

In addition, `colorReplacements` may contain scoped replacements. This is useful when you provide multiple themes and want to replace the colors of a specific theme:

```js
const html = await codeToHtml(
  code,
  {
    lang: 'js',
    themes: { dark: 'min-dark', light: 'min-light' },
    colorReplacements: {
      'min-dark': {
        '#ff79c6': '#189eff'
      },
      'min-light': {
        '#ff79c6': '#defdef'
      }
    }
  }
)
```

This is only allowed for the `colorReplacements` option and not for the theme object.

## CSS Variables Theme

Shiki provides a factory function helper `createCssVariablesTheme` for creating a theme that uses CSS variables easier. Note that this theme is a lot less granular than most of the other themes and requires to define the CSS variables in your app. This is provided for easier migration from Shiki's [`css-variables theme`](https://github.com/shikijs/shiki/blob/main/docs/themes.md#theming-with-css-variables). For better highlighting result, we recommend construct the theme manually with [Arbitrary Color Values](#arbitrary-color-values) or use [Color Replacements](#color-replacements) to override an existing theme.

This theme is **not included by default** and must be registered explicitly:

```
/* Detailed source-code truncated for AI context efficiency. */
```

CSS variables example:

```
/* Detailed source-code truncated for AI context efficiency. */
```

If you are migrating from Shiki v0, some variables are renamed:

| Shiki v0                   | Shiki v1.0           |
| -------------------------- | -------------------- |
| `--shiki-color-text`       | `--shiki-foreground` |
| `--shiki-color-background` | `--shiki-background` |
| `--shiki-color-ansi-*`     | `--shiki-ansi-*`     |

---

### Guide Transformers (`docs/guide/transformers.md`)

# Transformers

Shiki uses [`hast`](https://github.com/syntax-tree/hast), a AST format for HTML, to process the result and generate the HTML.

You can provide your own `transformers` to customize the generated HTML by manipulating the hast tree. You can pass custom functions to modify the tree for different types of nodes. For example:

```
/* Detailed source-code truncated for AI context efficiency. */
```

We also provide some common transformers for you to use, see [`@shikijs/transformers`](/packages/transformers) and [`@shikijs/colorized-brackets`](/packages/colorized-brackets) for more details.

## Transformer Hooks

```mermaid
flowchart LR
  preprocess --> tokens
  tokens --> span
  span --> span
  span --> line
  line --> span
  line --> code
  code --> pre
  pre --> root
  root --> postprocess
```

- `preprocess` - Called before the code is tokenized. You can use this to modify the code before it is tokenized.
- `tokens` - Called after the code is tokenized. You can use this to modify the tokens.
- `span` - Called for each `<span>` tag, for each token.
- `line` - Called for each line `<span>` tag.
- `code` - Called for each `<code>` tag, wraps all the lines.
- `pre` - Called for each `<pre>` tag, wraps the `<code>` tag.
- `root` - The root of HAST tree. Usually with only one child `<pre>` tag.
- `postprocess` - Called after the HTML is generated, get a chance to modify the final output. Will not be called in `codeToHast`.

## Meta

Transformers can also access markdown 'meta' strings in [supported integrations](/guide/install#integrations).



````markdown

```html foo=bar baz-qux="qu ux"
````

You can access the raw meta using:



```ts
this.options.meta
// => { __raw: 'foo=bar baz-qux="qu ux"' }
```

## Enforcing Transformer Ordering

For compatibility or fine-grained control, it may be necessary to enforce the order of a Shiki transformer. You can control the position of a transformer with the `enforce` modifier:

- `pre`: invoked before normal transformers
- default: invoked as a normal transformers
- `post`: invoked after normal transformers

Example:

```ts twoslash
// @twoslash-cache: {"v":1,"hash":"8d7b272af17166c83782bdb0d9e41de0f75c878526f69245c6e6021e1a4bfa45","data":"N4Igdg9gJgpgziAXAbVAFwJ4AcZJACwgDcYAnEAGhDRgA808AKAQwBsBLZuASgAJ2wNUgDNmAYxi8AyvnYBrdgBVSzMHGERSAWzIAdMOy1ZNaabIXLV6zTvJU4aZqQaIAjABYqrGGADmafCRXADYqR1JfGBcQGXklFTUNbTJKEA4wXEQABioxfCdxISD3AF8KdGxMgmIUsLposQg1UzEAVwcILUtEmzJEMzju62S7EAcnFwBWAGYvH39AtwB2MKdIhva0TqGk21T0zIAmXPyVMSLEYLKKnDxCElGaeiY2Th5+QTJRCQGLBOHbPpDMZnL94lZdrUxuEXEtjml5gEgqFqGsonhYn8Ib1RgckPC8gVzilEIdJtdqJU7jVHvUmFhSBAcM4MHxMeCeiMAHRgZg6AD8/QcpAEvl4AB9eK0wLBhAIYFBUlAIGIEIgQAA5PmSCDCXgBSRof6Q0bjZxIACckzmfiRiE8qIi6PVvJ0+3lSFmIEJZwuhwpmFu6vuUKe0UYDKZZEwbPMHIBZC5PiSEkFvF0IAZMAzEvTmYgDhzkulsvliqoytVeAAomAU5IsKxWr4BB8iCrmGh2E19ewyLw4IZ2KwnPqILwAO4wABGWHEcl4rAgzFgpDgXN4AAkIBA5HBeJpV6KgfuHMPWAPWtOAFYwc5j/X4SQAA0PZGfvEjzMwH0fkkIu4Hjed5oFy+j6AACk2LZgG2HZdj2b6kIg+gALS8M+yaaBI/QAORZrhH6Ns2AhwGhvCQNobCftBpHkZhdbYTAeHGA4hE0SRajkXAcafgWpjETBCD2DCQSHK4NoLEgKyOuseBYaQEjuhkSAOj6hQkv65SUkG1QPKkYb0oy36smCOw4lyjSwGmjCMAE7BwP07LmSMADCTRhhQvD5A4/TVt4OiCHwAC8AB8vD+TAgWmJK7bsFAfDFjKMByhk5YgJWaogC5Wh/hhAA8VkwKFH4wAFPigbwABKUStKQsHMBRMATrwGrQJIE7nrwpAwI24iGk+B4ijB1FNDAXKpGaLiuNMFqSXaDrhHJ6pFcpmTWt6pwaeQpIBlSwY0gZdLqhGBQ6EIfCQLAfnlYIk2iW47gOt4tqLItaLRFduBeB6iAbepxI7UsJQALq5O1eDAiY+qVLwwBmcaOK8CUvDCIyuW4TxcS4eBYCNM0vBtB0XSIyMTlxjl/bBXD+i8BR2p4VoGCoUa2IjLhFC07wCk4bw+E9RzXNFYwX18MAXN0wA9JLvBcnLXNlPoJSpOdzBIKA9Q+IOTR4GgCAlCUQA==="}
import type { ShikiTransformer } from 'shiki'

const customTransformer: ShikiTransformer = {
  name: 'my-transformer',
  enforce: 'pre',
  code(node) {
    // ...
  },
}
```

---

### Packages Astro (`docs/packages/astro.md`)

# Astro Integration

You don't need explicit integration in [Astro](https://astro.build/) as Astro already uses Shiki under the hood.

Learn more about the configurations in the [Astro documentation](https://docs.astro.build/en/guides/syntax-highlighting/).

---

### Packages Cli (`docs/packages/cli.md`)

# @shikijs/cli

<Badges name="@shikijs/cli" />

Shiki in the command line.

## Usage

The Shiki CLI works like `cat` command, but with syntax highlighting.
It also supports remote files.

```bash
npx @shikijs/cli README.md
```

```bash
npx @shikijs/cli \
  'https://github.com/shikijs/shiki/blob/main/taze.config.ts?raw=true'
```

## Install

You can also install it globally. Command aliases `@shikijs/cli`, `shiki`, `skat` are registered.

::: code-group

```sh [npm]
npm i @shikijs/cli
```

```sh [yarn]
yarn global add @shikijs/cli
```

```sh [pnpm]
pnpm add @shikijs/cli
```

```sh [bun]
bun add -g @shikijs/cli
```

```sh [deno]
deno install -gREn skat npm:@shikijs/cli
```

:::

```sh
skat src/index.ts
```

## Options

### `--theme`

Specify the theme to use. Defaults to `vitesse-dark`.

```bash
npx @shikijs/cli README.md --theme=nord
```

### `--lang`

Language is auto-inferred from the file extension. You can override it with `--lang`.

```bash
npx @shikijs/cli src/index.js --lang=ts
```

### `--format`

Specify the output format. Defaults to `ansi`.
Supported values: `ansi`, `html`.

```bash
npx @shikijs/cli README.md --format=html
```

## Node.js API

The `@shikijs/cli` package also provides a Node.js API.

::: code-group

```sh [npm]
npm i @shikijs/cli
```

```sh [yarn]
yarn add @shikijs/cli
```

```sh [pnpm]
pnpm add @shikijs/cli
```

```sh [bun]
bun add @shikijs/cli
```

```sh [deno]
deno add npm:@shikijs/cli
```

:::

### `codeToANSI`

The asynchronous `codeToANSI` function allows you to convert code to ANSI escape codes for terminal output.
This is useful for rendering syntax-highlighted code in the terminal.

```ts
import { codeToANSI } from '@shikijs/cli'

const highlighted = await codeToANSI(source, 'typescript', 'nord')

console.log(highlighted)
```

`codeToANSI` takes three required parameters:

1. `code: string`
2. `lang: BundledLanguage`
3. `theme: BundledTheme`

and one optional parameter:

4. `options?: CodeToANSIOptions`

The color level of the emitted escape codes is explicit instead of being inferred from the environment, and defaults to `3` (truecolor). Use `options.colorLevel` to target a different level — pass the level reported by `ansis` to follow the current terminal, or `0` to get plain text:

```ts
import { codeToANSI } from '@shikijs/cli'
import ansis from 'ansis'

const highlighted = await codeToANSI(source, 'typescript', 'nord', {
  colorLevel: ansis.level,
})
```

---

### Packages Codegen (`docs/packages/codegen.md`)

# shiki-codegen

<Badges name="shiki-codegen" />

Codegen for Shiki to create optimized bundles dedicated for your usage.

## Usage

### CLI

```bash
npx shiki-codegen \
  --langs typescript,javascript,vue \
  --themes light-plus,dark-plus \
  --engine javascript \
  ./shiki.bundle.ts
```

File `shiki.bundle.ts` will be created with the code that you can use in your project.

Then you can use it in your project:

```ts
import { codeToHtml } from './shiki.bundle'

const html = await codeToHtml(code, { lang: 'typescript', theme: 'light-plus' })
```

### Programmatic

You can also use `shiki-codegen` programmatically, and write the generated code to a file:

```ts
import { codegen } from 'shiki-codegen'

const { code } = await codegen({
  langs: ['typescript', 'javascript', 'vue'],
  themes: ['light-plus', 'dark-plus'],
  engine: 'javascript',
  typescript: true
})

// Write the code to a file
```

---

### Packages Colorized Brackets (`docs/packages/colorized-brackets.md`)

---
outline: deep
---

# @shikijs/colorized-brackets

<Badges name="@shikijs/colorized-brackets" />

VSCode-style colorized brackets transformer for Shiki.

## Install

::: code-group

```sh [npm]
npm i -D @shikijs/colorized-brackets
```

```sh [yarn]
yarn add -D @shikijs/colorized-brackets
```

```sh [pnpm]
pnpm add -D @shikijs/colorized-brackets
```

```sh [bun]
bun add -D @shikijs/colorized-brackets
```

```sh [deno]
deno add npm:@shikijs/colorized-brackets
```

:::

## Usage

Add to your Shiki transformers:

```ts colorize-brackets
import { transformerColorizedBrackets } from '@shikijs/colorized-brackets'
import { codeToHtml } from 'shiki'

const html = await codeToHtml('let values: number[] = [];', {
  lang: 'ts',
  theme: 'dark-plus',
  transformers: [transformerColorizedBrackets()],
})
```

### Colors

Brackets are automatically colored according to your Shiki theme (or themes if using [dual themes](https://shiki.style/guide/dual-themes)), with support for all of Shiki's built-in themes. However, you can customize colors if you've added custom themes to Shiki, or if you want to override the colors of a built-in theme:

```ts colorize-brackets
const html = await codeToHtml('let values: number[] = [];', {
  lang: 'ts',
  theme: myCustomTheme,
  transformers: [transformerColorizedBrackets({
    themes: {
      'my-custom-theme': ['goldenrod', 'blueviolet', 'dodgerblue', 'crimson'],
    },
  })],
})
```

The final color is the mismatched bracket color. The other colors are for each "level" of bracket pair. Any valid CSS color can be used.

If no bracket colors are found for a theme, it falls back to the default `dark-plus` theme.

### Brackets

You can customize the bracket pairs:

```ts colorize-brackets
const transformer = transformerColorizedBrackets({
  bracketPairs: [{ opener: '{', closer: '}' }],
})
```

The above would only colorize `{}` curly brackets. The default config colorizes `[]` square brackets, `{}` curly brackets, `()` parentheses, and `<>` angle brackets (only in TS type annotations).

For advanced usage, you can specify which TextMate scopes a bracket pair is allowed or denied in, using `scopesAllowList` and `scopesDenyList`. For example, the default config for `<>` angle brackets is:

```ts colorize-brackets
const bracketPair = {
  opener: '<',
  closer: '>',
  scopesAllowList: [
    'punctuation.definition.typeparameters.begin.ts',
    'punctuation.definition.typeparameters.end.ts',
  ],
}
```

### Language-specific Overrides

All settings can be overridden for specific languages using the `langs` option:

```ts colorize-brackets
const transformer = transformerColorizedBrackets({
  langs: { ts: myCustomTypescriptConfig },
})
```

### Explicit Trigger

If you do not want colorized brackets for all code blocks, you can enable the `explicitTrigger` option:

```ts colorize-brackets
const transformer = transformerColorizedBrackets({
  explicitTrigger: true,
})
```

Then, only code blocks with the `colorize-brackets` [meta string](/guide/transformers#meta) will have bracket colorizing enabled.

````md
```ts
// no bracket colorizing
```

```ts colorize-brackets
// brackets will be colorized
```
````

---

### Packages Magic Move (`docs/packages/magic-move.md`)

---
outline: deep
---

# @shikijs/magic-move

<Badges name="@shikijs/magic-move" />

Smoothly animated code blocks with Shiki. Useful for code-step animations in slide decks (e.g. [Slidev](https://sli.dev/guide/syntax#shiki-magic-move)) and tutorials.

`@shikijs/magic-move` is a low-level library: at its core is a framework-agnostic [diff/animation machine](https://github.com/shikijs/shiki/blob/main/packages/magic-move/src/core.ts) and [renderer](https://github.com/shikijs/shiki/blob/main/packages/magic-move/src/renderer.ts), with thin wrappers for Vue, React, Solid, Svelte, and a Web Component.

Each framework wrapper provides three components:

- **`ShikiMagicMove`** — the main component; wraps a code block and animates whenever `code` changes
- **`ShikiMagicMovePrecompiled`** — animations for compiled tokens, without the Shiki dependency at runtime
- **`ShikiMagicMoveRenderer`** — the low-level renderer component

`ShikiMagicMove` needs a Shiki highlighter instance plus the bundled stylesheet (`@shikijs/magic-move/style.css`). Whenever `code` changes, the component animates the diff.

## Install

::: code-group

```sh [npm]
npm i -D @shikijs/magic-move shiki
```

```sh [yarn]
yarn add -D @shikijs/magic-move shiki
```

```sh [pnpm]
pnpm add -D @shikijs/magic-move shiki
```

```sh [bun]
bun add -D @shikijs/magic-move shiki
```

```sh [deno]
deno add npm:@shikijs/magic-move npm:shiki
```

:::

## Usage

### Vue

Import `@shikijs/magic-move/vue` and pass the highlighter instance to the `ShikiMagicMove` component.

```vue
<script setup>
import { ShikiMagicMove } from '@shikijs/magic-move/vue'
import { createHighlighter } from 'shiki'
import { ref } from 'vue'

import '@shikijs/magic-move/style.css'

const highlighter = await createHighlighter({
  themes: ['nord'],
  langs: ['javascript', 'typescript'],
})

const code = ref(`const hello = 'world'`)

function animate() {
  code.value = `let hi = 'hello'`
}
</script>

<template>
  <ShikiMagicMove
    lang="ts"
    theme="nord"
    :highlighter="highlighter"
    :code="code"
    :options="{ duration: 800, stagger: 0.3, lineNumbers: true }"
  />
  <button @click="animate">
    Animate
  </button>
</template>
```

### React

Import `@shikijs/magic-move/react` and pass the highlighter instance to the `ShikiMagicMove` component.

```tsx
import type { HighlighterCore } from 'shiki'
import { ShikiMagicMove } from '@shikijs/magic-move/react'
import { useEffect, useState } from 'react'
import { createHighlighter } from 'shiki'

import '@shikijs/magic-move/style.css'

function App() {
  const [code, setCode] = useState(`const hello = 'world'`)
  const [highlighter, setHighlighter] = useState<HighlighterCore>()

  useEffect(() => {
    async function initializeHighlighter() {
      const h = await createHighlighter({
        themes: ['nord'],
        langs: ['javascript', 'typescript'],
      })
      setHighlighter(h)
    }
    initializeHighlighter()
  }, [])

  function animate() {
    setCode(`let hi = 'hello'`)
  }

  return (
    <div>
      {highlighter && (
        <>
          <ShikiMagicMove
            lang="ts"
            theme="nord"
            highlighter={highlighter}
            code={code}
            options={{ duration: 800, stagger: 0.3, lineNumbers: true }}
          />
          <button onClick={animate}>Animate</button>
        </>
      )}
    </div>
  )
}
```

### Solid

Import `@shikijs/magic-move/solid` and pass the highlighter instance to the `ShikiMagicMove` component.

```tsx
import { ShikiMagicMove } from '@shikijs/magic-move/solid'
import { bundledLanguages, bundledThemes, createHighlighter } from 'shiki'
import { createResource, createSignal, Show } from 'solid-js'

import '@shikijs/magic-move/style.css'

function App() {
  const [code, setCode] = createSignal(`const hello = 'world'`)

  const [highlighter] = createResource(async () => {
    return await createHighlighter({
      themes: Object.keys(bundledThemes),
      langs: Object.keys(bundledLanguages),
    })
  })

  function animate() {
    setCode(`let hi = 'hello'`)
  }

  return (
    <div>
      <Show when={highlighter()}>
        {h => (
          <>
            <ShikiMagicMove
              lang="ts"
              theme="nord"
              highlighter={h()}
              code={code()}
              options={{ duration: 800, stagger: 0.3, lineNumbers: true }}
            />
            <button onClick={animate}>Animate</button>
          </>
        )}
      </Show>
    </div>
  )
}
```

### Svelte

Import `@shikijs/magic-move/svelte` and pass the highlighter instance to the `ShikiMagicMove` component.

```svelte
<script lang="ts">
  import { ShikiMagicMove } from '@shikijs/magic-move/svelte'
  import { createHighlighter } from 'shiki'

  import '@shikijs/magic-move/style.css'

  const highlighter = createHighlighter({
    themes: ['nord'],
    langs: ['javascript', 'typescript'],
  })

  let code = $state(`const hello = 'world'`)

  function animate() {
    code = `let hi = 'hello'`
  }
</script>

{#await highlighter then highlighter}
  <ShikiMagicMove
    lang="ts"
    theme="nord"
    {highlighter}
    {code}
    options={{ duration: 800, stagger: 0.3, lineNumbers: true }}
  />
  <button onclick={animate}>Animate</button>
{/await}
```

## `ShikiMagicMovePrecompiled`

`ShikiMagicMovePrecompiled` is a lighter variant of `ShikiMagicMove` that doesn't need Shiki at runtime — useful when you want to ship pre-tokenized step data (e.g. produced at build time) and animate between steps in the browser.

```vue
<script setup>
import { ShikiMagicMovePrecompiled } from '@shikijs/magic-move/vue'
import { ref } from 'vue'

const step = ref(1)
const compiledSteps = [/* Compiled token steps */]
</script>

<template>
  <ShikiMagicMovePrecompiled
    :steps="compiledSteps"
    :step="step"
  />
  <button @click="step++">
    Next
  </button>
</template>
```

To produce the compiled tokens, run this somewhere with access to Shiki (build script, server, etc.) and serialize the result:

```ts
import { codeToKeyedTokens, createMagicMoveMachine } from '@shikijs/magic-move/core'
import { createHighlighter } from 'shiki'

const shiki = await createHighlighter({
  themes: ['nord'],
  langs: ['javascript', 'typescript'],
})

const codeSteps = [
  `const hello = 'world'`,
  `let hi = 'hello'`,
]

const machine = createMagicMoveMachine(
  code => codeToKeyedTokens(shiki, code, {
    lang: 'ts',
    theme: 'nord',
  }),
  {
    // options
  },
)

const compiledSteps = codeSteps.map(code => machine.commit(code).current)

// Pass `compiledSteps` to the precompiled component.
// Since these are plain serialisable objects, you can stringify them at build time
// and rehydrate them in the browser without needing Shiki.
```

## How it works

For a deep dive into the algorithm behind the animations, see Anthony's article [**The Magic In Shiki Magic Move**](https://antfu.me/posts/shiki-magic-move).

---

### Packages Markdown Exit (`docs/packages/markdown-exit.md`)

# @shikijs/markdown-exit

<Badges name="@shikijs/markdown-exit" />

[markdown-exit](https://github.com/serkodev/markdown-exit) plugin for Shiki.

`markdown-exit` is a modern TypeScript rewrite of `markdown-it` with native async rendering support — no extra library needed for async highlighting.

## Install

::: code-group

```sh [npm]
npm i -D @shikijs/markdown-exit
```

```sh [yarn]
yarn add -D @shikijs/markdown-exit
```

```sh [pnpm]
pnpm add -D @shikijs/markdown-exit
```

```sh [bun]
bun add -D @shikijs/markdown-exit
```

```sh [deno]
deno add npm:@shikijs/markdown-exit
```

:::

## Usage

The default export uses Shiki's [shorthands](/guide/shorthands) for on-demand loading of themes and languages. Since highlighting is async, use `md.renderAsync()` to render:

````ts
import Shiki from '@shikijs/markdown-exit'
import { createMarkdownExit } from 'markdown-exit'

const md = createMarkdownExit()

md.use(Shiki({
  themes: {
    light: 'vitesse-light',
    dark: 'vitesse-dark',
  }
}))

// Use `md.renderAsync` instead of `md.render`
const html = await md.renderAsync('# Title\n```ts\nconsole.log("Hello, World!")\n```')
````

## Transformers

You can pass [transformers](/guide/transformers) to the plugin options to customize the highlighted code.

```ts
import Shiki from '@shikijs/markdown-exit'
import { transformerNotationDiff } from '@shikijs/transformers'
import { createMarkdownExit } from 'markdown-exit'

const md = createMarkdownExit()

md.use(Shiki({
  themes: {
    light: 'vitesse-light',
    dark: 'vitesse-dark',
  },
  transformers: [
    transformerNotationDiff(),
  ],
}))

const html = await md.renderAsync(md_string)
```

## Custom `codeToHtml`

If you have a custom `codeToHtml` function (e.g., from a fine-grained shorthand bundle), use `fromAsyncCodeToHtml`:

````ts
import { fromAsyncCodeToHtml } from '@shikijs/markdown-exit'
import { createMarkdownExit } from 'markdown-exit'
import { codeToHtml } from 'shiki' // Or your custom shorthand bundle

const md = createMarkdownExit()

md.use(
  fromAsyncCodeToHtml(
    codeToHtml,
    {
      themes: {
        light: 'vitesse-light',
        dark: 'vitesse-dark',
      }
    }
  )
)

// Use `md.renderAsync` instead of `md.render`
const html = await md.renderAsync('# Title\n```ts\nconsole.log("Hello, World!")\n```')
````

## Fine-grained Bundle

By default, the full bundle of `shiki` will be imported. If you are using a [fine-grained bundle](/guide/bundles#fine-grained-bundle), you can import from `@shikijs/markdown-exit/core` and pass your own highlighter:

```ts
import { fromHighlighter } from '@shikijs/markdown-exit/core'
import { createMarkdownExit } from 'markdown-exit'
import { createHighlighterCore } from 'shiki/core'
import { createOnigurumaEngine } from 'shiki/engine/oniguruma'

const highlighter = await createHighlighterCore({
  themes: [
    import('@shikijs/themes/vitesse-light')
  ],
  langs: [
    import('@shikijs/langs/javascript'),
  ],
  engine: createOnigurumaEngine(() => import('shiki/wasm'))
})

const md = createMarkdownExit()

md.use(fromHighlighter(highlighter, { /* options */ }))
```

---

### Packages Markdown It (`docs/packages/markdown-it.md`)

# @shikijs/markdown-it

<Badges name="@shikijs/markdown-it" />

[markdown-it](https://markdown-it.github.io/) plugin for Shiki.

## Install

::: code-group

```sh [npm]
npm i -D @shikijs/markdown-it
```

```sh [yarn]
yarn add -D @shikijs/markdown-it
```

```sh [pnpm]
pnpm add -D @shikijs/markdown-it
```

```sh [bun]
bun add -D @shikijs/markdown-it
```

```sh [deno]
deno add npm:@shikijs/markdown-it
```

:::

## Usage

```
/* Detailed source-code truncated for AI context efficiency. */
```

## Transformers

You can pass [transformers](/guide/transformers) to the plugin options to customize the highlighted code.

```
/* Detailed source-code truncated for AI context efficiency. */
```

## Fine-grained Bundle

By default, the full bundle of `shiki` will be imported. If you are using a [fine-grained bundle](/guide/bundles#fine-grained-bundle), you can import from `@shikijs/markdown-it/core` and pass your own highlighter:

```
/* Detailed source-code truncated for AI context efficiency. */
```

## With Shorthands

Shiki's [shorthands](/guide/shorthands) provides on-demand loading of themes and languages, but also makes the highlighting process asynchronous. Unfortunately, `markdown-it` itself [does NOT support async highlighting](https://github.com/markdown-it/markdown-it/blob/master/docs/development.md#i-need-async-rule-how-to-do-it) out of the box.

To workaround this, you can use [`markdown-it-async`](https://github.com/antfu/markdown-it-async) by [Anthony Fu](https://github.com/antfu). Where Shiki also provides an integration with it, you can import `fromAsyncCodeToHtml` from `@shikijs/markdown-it/async`.

```
/* Detailed source-code truncated for AI context efficiency. */
```ts\nconsole.log("Hello, World!")\n```')
````

## Transformer Caveats

`markdown-it` defaults to enforcing `<pre><code>` as the outermost wrappers of code block html. If you use a custom Shiki [transformer](/guide/transformers), this behavior may be undesirable. For example, if the transformer produces

```html
<div class="fenced-code-block">
  <pre>
    <code>
      …
    </code>
  </pre>
</div>
```

the result after `markdown-it` processing will be

```html
<pre>
  <code>
    <div class="fenced-code-block">
      <pre>
        <code>
          …
        </code>
      </pre>
    </div>
  </code>
</pre>
```

Work around this by adding [olets/markdown-it-wrapperless-fence-rule](https://github.com/olets/markdown-it-wrapperless-fence-rule) to your `markdown-it` configuration, or by writing your own `markdown-it` fence rule (see [markdown-it#269](https://github.com/markdown-it/markdown-it/issues/269)).

---

### Packages Monaco (`docs/packages/monaco.md`)

# Monaco Editor Integration

Shiki uses the same TextMate grammar and themes as VS Code, so it can be used to highlight Monaco Editor. Shiki provides an official integration for Monaco Editor. You can also use [modern-monaco](https://github.com/esm-dev/modern-monaco) that includes built-in Shiki integration.

## @shikijs/monaco

<Badges name="@shikijs/monaco" />

Use Shiki to highlight [Monaco Editor](https://microsoft.github.io/monaco-editor/).

Monaco's built-in highlighter does not use the full TextMate grammar, which in some cases is not accurate enough. This package allows you to use Shiki's syntax highlighting engine to highlight Monaco, with shared grammars and themes from Shiki.

Heavily inspired by [`monaco-editor-textmate`](https://github.com/zikaari/monaco-editor-textmate).

### Install

::: code-group

```sh [npm]
npm i -D @shikijs/monaco
```

```sh [yarn]
yarn add -D @shikijs/monaco
```

```sh [pnpm]
pnpm add -D @shikijs/monaco
```

```sh [bun]
bun add -D @shikijs/monaco
```

```sh [deno]
deno add npm:@shikijs/monaco
```

:::

### Usage

```ts
import { shikiToMonaco } from '@shikijs/monaco'
import * as monaco from 'monaco-editor-core'
import { createHighlighter } from 'shiki'

// Create the highlighter, it can be reused
const highlighter = await createHighlighter({
  themes: [
    'vitesse-dark',
    'vitesse-light',
  ],
  langs: [
    'javascript',
    'typescript',
    'vue'
  ],
})

// Register the languageIds first. Only registered languages will be highlighted.
monaco.languages.register({ id: 'vue' })
monaco.languages.register({ id: 'typescript' })
monaco.languages.register({ id: 'javascript' })

// Register the themes from Shiki, and provide syntax highlighting for Monaco. // [!code highlight:2]
shikiToMonaco(highlighter, monaco)

// Create the editor
const editor = monaco.editor.create(document.getElementById('container'), {
  value: 'const a = 1',
  language: 'javascript',
  theme: 'vitesse-dark',
})

// ...As you use the editor normally
```

## modern-monaco

<Badges name="modern-monaco" />

We highly recommend using [modern-monaco](https://github.com/esm-dev/modern-monaco) that includes built-in Shiki integration. It provides a more convenient API for building Monaco Editor.

### Install

::: code-group

```sh [npm]
npm i -D modern-monaco
```

```sh [yarn]
yarn add -D modern-monaco
```

```sh [pnpm]
pnpm add -D modern-monaco
```

```sh [bun]
bun add -D modern-monaco
```

```sh [deno]
deno add npm:modern-monaco
```

:::

Or import it from [esm.sh](https://esm.sh) CDN in the browser without a build step:

```js
import * as monaco from 'https://esm.sh/modern-monaco'
```

### Usage

```html

<monaco-editor theme="vitesse-dark"></monaco-editor>
<script src="app.js" type="module"></script>
```

```js
// app.js
import { lazy, Workspace } from 'modern-monaco'

// create a workspace with initial files
const workspace = new Workspace({
  initialFiles: {
    'index.html': `<html><body>...</body></html>`,
    'main.js': `console.log('Hello, world!')`,
  },
  entryFile: 'index.html',
})

// initialize the editor lazily
await lazy({ workspace })

// write a file and open it in the editor
workspace.fs.writeFile('util.js', 'export function add(a, b) { return a + b; }')
workspace.openTextDocument('util.js')
```

More usage please see the [modern-monaco](https://github.com/esm-dev/modern-monaco) repository.

---

### Packages Next (`docs/packages/next.md`)

# Next.js

Shiki does not provide an official integration for [Next.js](https://nextjs.org), but it is rather straightforward to use Shiki in Next.js applications.

::: info
Using Shiki on Edge Runtime might cause unintended problems, Shiki relies on lazy imports to load languages and themes.

Serverless Runtime is recommended.
:::

## React Server Component

Since Server Components are server-only, you can use the bundled highlighter without worrying the bundle size.

```tsx
import type { BundledLanguage } from 'shiki'
import { codeToHtml } from 'shiki'

export default function Page() {
  return (
    <main>
      <CodeBlock lang="ts">
        {[
          'console.log("Hello")',
          'console.log("World")',
        ].join('\n')}
      </CodeBlock>
    </main>
  )
}

interface Props {
  children: string
  lang: BundledLanguage
}

async function CodeBlock(props: Props) {
  const out = await codeToHtml(props.children, {
    lang: props.lang,
    theme: 'github-dark'
  })

  return <div dangerouslySetInnerHTML={{ __html: out }} />
}
```

### Custom Components

You can also call `codeToHast` to get the HTML abstract syntax tree, and render it using [`hast-util-to-jsx-runtime`](https://github.com/syntax-tree/hast-util-to-jsx-runtime). With this method, you can render your own `pre` and `code` components.

```tsx
import type { JSX } from 'react'
import type { BundledLanguage } from 'shiki'
import { toJsxRuntime } from 'hast-util-to-jsx-runtime'
import { Fragment } from 'react'
import { jsx, jsxs } from 'react/jsx-runtime'
import { codeToHast } from 'shiki'

export default function Page() {
  return (
    <main>
      <CodeBlock lang="ts">
        {[
          'console.log("Hello")',
          'console.log("World")',
        ].join('\n')}
      </CodeBlock>
    </main>
  )
}

interface Props {
  children: string
  lang: BundledLanguage
}

async function CodeBlock(props: Props) {
  const out = await codeToHast(props.children, {
    lang: props.lang,
    theme: 'github-dark'
  })

  return toJsxRuntime(out, {
    Fragment,
    jsx,
    jsxs,
    components: {
      // your custom `pre` element
      pre: props => <pre data-custom-codeblock {...props} />
    },
  }) as JSX.Element
}
```

## React Client Component

For client components, they are pre-rendered on server and hydrated/rendered on client.
We can start by creating a client `CodeBlock` component.

Create a `shared.ts` for highlighter:

```ts
import type { JSX } from 'react'
import type { BundledLanguage } from 'shiki/bundle/web'
import { toJsxRuntime } from 'hast-util-to-jsx-runtime'
import { Fragment } from 'react'
import { jsx, jsxs } from 'react/jsx-runtime'
import { codeToHast } from 'shiki/bundle/web'

export async function highlight(code: string, lang: BundledLanguage) {
  const out = await codeToHast(code, {
    lang,
    theme: 'github-dark'
  })

  return toJsxRuntime(out, {
    Fragment,
    jsx,
    jsxs,
  }) as JSX.Element
}
```

In your `codeblock.tsx`:

```tsx
'use client'
import { JSX, useLayoutEffect, useState } from 'react'
import { highlight } from './shared'

export function CodeBlock({ initial }: { initial?: JSX.Element }) {
  const [nodes, setNodes] = useState(initial)

  useLayoutEffect(() => {
    void highlight('console.log("Rendered on client")', 'ts').then(setNodes)
  }, [])

  return nodes ?? <p>Loading...</p>
}
```

The `initial` prop can be passed from a server component to pre-render the code block on server.

In your `page.tsx`:

```tsx
import { CodeBlock } from './codeblock'
import { highlight } from './shared'

export default async function Page() {
  // `initial` is optional.
  return (
    <main>
      <CodeBlock initial={await highlight('console.log("Rendered on server")', 'ts')} />
    </main>
  )
}
```

::: info
The above example uses the `shiki/bundle/web` bundle. You can change it to [Fine-grained Bundle](/guide/bundles#fine-grained-bundle) to fully control the bundled languages/themes.
:::

### Performance

Shiki lazy loads the requested languages and themes, the Next.js bundler can handle lazy imports automatically.
Importing `shiki` or its web bundle is efficient enough for most Next.js applications, Fine-grained Bundle won't significantly impact the bundle size.

In addition, you can use the `createHighlighter` API to preload specific languages and themes.
Please refer to [Highlighter Usage](/guide/install#highlighter-usage) for further details.

### Highlighter Instance

If you define a highlighter (without `await`) as a global variable, you can reference it directly from server and client components.

```ts
import { createHighlighter } from 'shiki'

const highlighter = createHighlighter({
  themes: ['nord'],
  langs: ['javascript'],
})

// Inside an async server component, or client side `useEffect`
const html = (await highlighter).codeToHtml('const a = 1', {
  lang: 'javascript',
  theme: 'nord'
})
```

---

### Packages Nuxt (`docs/packages/nuxt.md`)

# Nuxt

[Nuxt](https://nuxt.com) is a general-purpose meta-framework that gives you full control over your application. You can install Shiki along with integrations like ([markdown-it](/packages/markdown-it), [rehype](/packages/rehype)) or import manually as you needed.

On top of that, Nuxt also provide some module integrations for Shiki allowing you use it easier:

- [`nuxt-shiki`](https://github.com/pi0/nuxt-shiki) - Shiki integration for client and server sides, with component usage.
- [`@nuxt/content`](https://github.com/nuxt/content) - Nuxt Content has Shiki built-in for markdown files. [You can enable it with the `highlight` option](https://content.nuxt.com/docs/getting-started/configuration#highlight).
  - [`@nuxtjs/mdc`](https://github.com/nuxt-modules/mdc) - The underlying module of `@nuxt/content` that provides Markdown Component (MDC) syntax as well as Shiki for syntax highlighting.

## TwoSlash Integration

- [`nuxt-content-twoslash`](https://github.com/antfu/nuxt-content-twoslash) - A Nuxt module that adds Twoslash support to Nuxt Content. Twoslash information are evaluated at build time.

---

### Packages Rehype (`docs/packages/rehype.md`)

---
outline: deep
---

# @shikijs/rehype

<Badges name="@shikijs/rehype" />

[rehype](https://github.com/rehypejs/rehype) plugin for Shiki.

## Install

::: code-group

```sh [npm]
npm i -D @shikijs/rehype
```

```sh [yarn]
yarn add -D @shikijs/rehype
```

```sh [pnpm]
pnpm add -D @shikijs/rehype
```

```sh [bun]
bun add -D @shikijs/rehype
```

```sh [deno]
deno add npm:@shikijs/rehype
```

:::

## Usage

```
/* Detailed source-code truncated for AI context efficiency. */
```

The default export of `@shikijs/rehype` uses a shared instance of `shiki` from `getSingletonHighlighter`, which will persist across processes. If you want full control over the highlighter lifecycle, use [Fine-grained Bundle `@shikijs/rehype/core`](#fine-grained-bundle) instead.

## Fine-grained Bundle

By default, the full bundle of `shiki` will be imported. If you are using a [fine-grained bundle](/guide/bundles#fine-grained-bundle), you can import `rehypeShikiFromHighlighter` from `@shikijs/rehype/core` and pass your own highlighter:

```
/* Detailed source-code truncated for AI context efficiency. */
```

## Features

### Inline Code

You can also highlight inline codes with the `inline` option.

| Option                  | Example          | Description                                                 |
| ----------------------- | ---------------- | ----------------------------------------------------------- |
| `false`                 | -                | Disable inline code highlighting (default)                  |
| `'tailing-curly-colon'` | `let a = 1{:js}` | Highlight with a `{:language}` marker inside the code block |

Enable `inline` on the Rehype plugin:

```
/* Detailed source-code truncated for AI context efficiency. */
```

Then you can use inline code in markdown:

```md
This code `console.log("Hello World"){:js}` will be highlighted.
```

## Postprocess Transformers

::: info
The `postprocess` transformer hook is only invoked when producing HTML strings (i.e., when using `codeToHtml`). Since `@shikijs/rehype` operates on HAST (Hypertext Abstract Syntax Tree) rather than HTML strings, postprocess transformers will not be executed.
:::

This design is intentional: running `postprocess` hooks in rehype would require converting HAST → HTML → running postprocess → parsing back to HAST, which changes the semantics and could surprise users expecting HAST-only transformers.

### Workaround for HTML-based Postprocessing

If you need to run HTML-based postprocessing with rehype, you can apply a `root` transformer that:

1. Converts the HAST fragment to HTML using `hast-util-to-html`
2. Runs your HTML transformers
3. Converts back to HAST using `hast-util-from-html`

Example:

```ts
import { fromHtml } from 'hast-util-from-html'
import { toHtml } from 'hast-util-to-html'

const file = await unified()
  .use(remarkParse)
  .use(remarkRehype)
  .use(rehypeShiki, {
    themes: {
      light: 'vitesse-light',
      dark: 'vitesse-dark',
    },
    transformers: [
      {
        name: 'custom-html-postprocessor',
        root(node) {
          // Convert HAST to HTML
          const html = toHtml(node)

          // Apply your HTML transformations
          const processedHtml = myCustomPostprocess(html)

          // Parse back to HAST
          const newNode = fromHtml(processedHtml, { fragment: true })

          // Replace the node
          return newNode
        }
      }
    ]
  })
  .use(rehypeStringify)
  .process(await fs.readFile('./input.md'))
```

---

### Packages Stream (`docs/packages/stream.md`)

---
outline: deep
---

# @shikijs/stream

<Badges name="@shikijs/stream" />

Streaming colorization for Shiki. Useful for highlighting text streams like LLM outputs, where code arrives incrementally and you want each chunk highlighted as it lands without re-tokenizing the whole document.

## Install

::: code-group

```sh [npm]
npm i -D @shikijs/stream
```

```sh [yarn]
yarn add -D @shikijs/stream
```

```sh [pnpm]
pnpm add -D @shikijs/stream
```

```sh [bun]
bun add -D @shikijs/stream
```

```sh [deno]
deno add npm:@shikijs/stream
```

:::

## Usage

Create a transform stream with `CodeToTokenTransformStream` and `.pipeThrough` your text stream:

```ts
import { CodeToTokenTransformStream } from '@shikijs/stream'
import { createHighlighter, createJavaScriptRegexEngine } from 'shiki'

// Initialize the Shiki highlighter somewhere in your app
const highlighter = await createHighlighter({
  langs: [/* ... */],
  themes: [/* ... */],
  engine: createJavaScriptRegexEngine(),
})

// The ReadableStream<string> you want to highlight
const textStream = getTextStreamFromSomewhere()

// Pipe the text stream through the token stream
const tokensStream = textStream
  .pipeThrough(new CodeToTokenTransformStream({
    highlighter,
    lang: 'javascript',
    theme: 'nord',
    allowRecalls: true, // see explanation below
  }))
```

### `allowRecalls`

Due to the fact that highlighting may change based on the context of the code, themed tokens can be re-evaluated as the stream progresses. Because streams are one-directional, `@shikijs/stream` emits a special **recall** token that tells the receiver to discard the last N tokens that changed.

By default, `CodeToTokenTransformStream` only emits **stable** tokens — no recalls. The trade-off is coarser granularity, usually one line at a time.

For stream consumers that can handle recalls (e.g. the bundled Vue, React, Solid, and Svelte components), set `allowRecalls: true` to get finer-grained tokens.

Typically recalls should be handled like:

```ts
import type { RecallToken } from '@shikijs/stream'
import type { ThemedToken } from 'shiki'

const receivedTokens: ThemedToken[] = []

tokensStream.pipeTo(new WritableStream<ThemedToken | RecallToken>({
  async write(token) {
    if ('recall' in token) {
      // discard the last `token.recall` tokens
      receivedTokens.length -= token.recall
    }
    else {
      receivedTokens.push(token)
    }
  },
}))
```

## Consume the Token Stream

### Manually

```ts
tokensStream.pipeTo(new WritableStream({
  async write(token) {
    console.log(token)
  },
}))
```

Or in Node.js:

```ts
for await (const token of tokensStream) {
  console.log(token)
}
```

### Vue

```vue
<script setup lang="ts">
import { ShikiStreamRenderer } from '@shikijs/stream/vue'

// get the token stream
</script>

<template>
  <ShikiStreamRenderer :stream="tokensStream" />
</template>
```

### React

```tsx
import { ShikiStreamRenderer } from '@shikijs/stream/react'

export function MyComponent() {
  // get the token stream
  return <ShikiStreamRenderer stream={tokensStream} />
}
```

### Solid

```tsx
import { ShikiStreamRenderer } from '@shikijs/stream/solid'

export function MyComponent() {
  // get the token stream
  return <ShikiStreamRenderer stream={tokensStream} />
}
```

### Svelte

```svelte
<script lang="ts">
import { ShikiStreamRenderer } from '@shikijs/stream/svelte'

// get the token stream
</script>

<ShikiStreamRenderer stream={tokensStream} />
```

## Cached Renderer

`@shikijs/stream` also ships a simplified renderer for the common case of an incrementally-updated code string (rather than a token stream).

::: warning Experimental
This API is experimental and may change.
:::

### Vue

```vue
<script setup lang="ts">
import { ShikiCachedRenderer } from '@shikijs/stream/vue'
import { createHighlighter, createJavaScriptRegexEngine } from 'shiki'
import { onMounted, ref } from 'vue'

const highlighter = await createHighlighter({
  langs: [/* ... */],
  themes: [/* ... */],
  engine: createJavaScriptRegexEngine(),
})

const code = ref('') // code should only be updated incrementally

// for demo purposes
onMounted(() => {
  setInterval(() => {
    code.value += '\nconsole.log("Hello, world!");'
  }, 1000)
})
</script>

<template>
  <ShikiCachedRenderer
    :highlighter="highlighter"
    :code="code"
    lang="js"
    theme="vitesse-light"
  />
</template>
```

---

### Packages Transformers (`docs/packages/transformers.md`)

---
outline: deep
---

# @shikijs/transformers

<Badges name="@shikijs/transformers" />

Common transformers for Shiki, inspired by [shiki-processor](https://github.com/innocenzi/shiki-processor).

## Install

::: code-group

```sh [npm]
npm i -D @shikijs/transformers
```

```sh [yarn]
yarn add -D @shikijs/transformers
```

```sh [pnpm]
pnpm add -D @shikijs/transformers
```

```sh [bun]
bun add -D @shikijs/transformers
```

```sh [deno]
deno add npm:@shikijs/transformers
```

:::

## Usage

```
/* Detailed source-code truncated for AI context efficiency. */
```

## Unstyled

Transformers only applies classes and does not come with styles; you can provide your own CSS rules to style them properly.

## Matching Algorithm

We found that the algorithm for matching comments in v1 is sometime conterintuitive, where we are trying to fix it in a progressive way. Since v1.29.0, we introduced a new `matchAlgorithm` option to most of the transformer for you to toggle between different matching algorithms. Right now, the default is `v1` which is the old algorithm, and `v3` is the new algorithm. When Shiki v3 is landed, the default will be `v3`.

```ts
const html = await codeToHtml(code, {
  lang: 'ts',
  theme: 'nord',
  transformers: [
    transformerNotationDiff({
      matchAlgorithm: 'v3', // [!code hl]
    }),
  ],
})
```

### `matchAlgorithm: 'v1'`

The matching algorithm mostly affects the single-line comment matching, in `v1`, it will count the comment line as the first line, while in `v3`, it will count start from the comment line:

```ts
// [\!code highlight:3]
console.log('highlighted') // [!code hl]
console.log('highlighted') // [!code hl]
console.log('not highlighted')
```

### `matchAlgorithm: 'v3'`

In `v3`, the matching algorithm will start counting from the line below the comment:

```ts
// [\!code highlight:2]
console.log('highlighted') // [!code hl]
console.log('highlighted') // [!code hl]
console.log('not highlighted')
```

## Transformers

### `transformerNotationDiff`

Use `[!code ++]` and `[!code --]` to mark added and removed lines.

````md
```ts
console.log('hewwo') // [\!code --]
console.log('hello') // [\!code ++]
console.log('goodbye')
```
````

Renders (with custom CSS rules):

```ts
console.log('hewwo') // [!code --]
console.log('hello') // [!code ++]
console.log('goodbye')
```

- `// [!code ++]` outputs: `<span class="line diff add">`
- `// [!code --]` outputs: `<span class="line diff remove">`
- The outer `<pre>` tag is modified: `<pre class="has-diff">`

::: details HTML Output

```html

<pre class="shiki has-diff"> 
  <code>
    <span class="line"></span>
    <span class="line"><span>function</span><span>()</span><span></span><span>{</span></span>
    <span class="line diff remove">  
      <span></span><span>console</span><span>.</span><span>log</span><span>(</span><span>&#39;</span><span>hewwo</span><span>&#39;</span><span>) </span>
    </span>
    <span class="line diff add">  
      <span></span><span>console</span><span>.</span><span>log</span><span>(</span><span>&#39;</span><span>hello</span><span>&#39;</span><span>) </span>
    </span>
    <span class="line"><span></span><span>}</span></span>
    <span class="line"><span></span></span>
  </code>
</pre>
```

:::

---

### `transformerNotationHighlight`

Use `[!code highlight]` to highlight a line.

````md
```ts
console.log('Not highlighted')
console.log('Highlighted') // [\!code highlight]
console.log('Not highlighted')
```
````

Renders (with custom CSS rules):

```ts
console.log('Not highlighted')
console.log('Highlighted') // [!code highlight]
console.log('Not highlighted')
```

- `// [!code highlight]` outputs: `<span class="line highlighted">`
- The outer `<pre>` tag is modified: `<pre class="has-highlighted">`

You can also highlight multiple lines with a single comment:

````md
```ts
// [\!code highlight:3]
console.log('Highlighted')
console.log('Highlighted')
console.log('Highlighted')
console.log('Not highlighted')
```

```ts
console.log('Not highlighted')
// [\!code highlight:1]
console.log('Highlighted')
console.log('Not highlighted')
```
````

Renders:

```ts
// [!code highlight:3]
console.log('Highlighted')
console.log('Highlighted')
console.log('Highlighted')
console.log('Not highlighted')
```

```ts
console.log('Not highlighted')
// [!code highlight:1]
console.log('Highlighted')
console.log('Not highlighted')
```

---

### `transformerNotationWordHighlight`

Use `[!code word:Hello]` to highlight the word `Hello` in any subsequent code.

````md
```ts
// [\!code word:Hello]
const message = 'Hello World'
console.log(message) // prints Hello World
```
````

Renders (with custom CSS rules):

```ts
// [!code word:Hello]
const message = 'Hello World'
console.log(message) // prints Hello World
```

Outputs: `<span class="highlighted-word">Hello</span>` for matched words.

You can also specify the number of lines to highlight words on, e.g. `[!code word:Hello:1]` will only highlight occurrences of `Hello` on the next line.

````md
```ts
// [\!code word:Hello:1]
const message = 'Hello World'
console.log(message) // prints Hello World
```
````

Renders:

```ts
// [!code word:Hello:1]
const message = 'Hello World'
console.log(message) // prints Hello World
```

---

### `transformerNotationFocus`

Use `[!code focus]` to focus a line.

````md
```ts
console.log('Not focused');
console.log('Focused') // [\!code focus]
console.log('Not focused');
```
````

Renders (with custom CSS rules):

```ts
console.log('Not focused')
console.log('Focused') // [!code focus]
console.log('Not focused')
```

- Outputs: `<span class="line focused">`
- The outer `<pre>` tag is modified: `<pre class="has-focused">`

You can also focus multiple lines with a single comment:

````md
```ts
// [\!code focus:3]
console.log('Focused')
console.log('Focused')
console.log('Focused')
console.log('Not focused')
```
````

Renders:

```ts
// [!code focus:3]
console.log('Focused')
console.log('Focused')
console.log('Focused')
console.log('Not focused')
```

---

### `transformerNotationErrorLevel`

Use `[!code error]`, `[!code warning]`, and `[!code info]` to mark a line with an error, warning, or info levels.

````md
```ts
console.log('No errors or warnings')
console.error('Error') // [\!code error]
console.warn('Warning') // [\!code warning]
console.log('Info') // [\!code info]
```
````

- Outputs: `<span class="line highlighted error">` for errors
- Outputs: `<span class="line highlighted warning">` for warnings
- Outputs: `<span class="line highlighted info">` for info
- The outer `<pre>` tag is modified: `<pre class="has-highlighted">`

With some additional CSS rules, you can make it look like this:

```ts
console.log('No errors or warnings')
console.error('Error') // [!code error]
console.warn('Warning') // [!code warning]
console.log('Info') // [!code info]
```

---

### `transformerRenderWhitespace`

Render whitespaces (tabs and spaces) as individual spans, with classes `tab` and `space`.

Options:

- `position`: `'all' | 'boundary' | 'trailing' | 'leading'`. Default `'all'`.

With some additional CSS rules, you can make it look like this:

<div class="language-js vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">js</span><pre v-pre class="shiki shiki-themes vitesse-light vitesse-dark" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212;" tabindex="0"><code><span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676;">function</span><span class="space"> </span><span style="--shiki-light:#59873A;--shiki-dark:#80A665;">block</span><span style="--shiki-light:#999999;--shiki-dark:#666666;">(</span><span class="space"> </span><span style="--shiki-light:#999999;--shiki-dark:#666666;">)</span><span class="space"> </span><span style="--shiki-light:#999999;--shiki-dark:#666666;">{</span></span>
<span class="line"><span class="space"> </span><span class="space"> </span><span style="--shiki-light:#59873A;--shiki-dark:#80A665;">space</span><span style="--shiki-light:#999999;--shiki-dark:#666666;">(</span><span class="space"> </span><span style="--shiki-light:#999999;--shiki-dark:#666666;">)</span></span>
<span class="line"><span class="tab">&#9;</span><span class="tab">&#9;</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665;">tab</span><span style="--shiki-light:#999999;--shiki-dark:#666666;">(</span><span class="space"> </span><span style="--shiki-light:#999999;--shiki-dark:#666666;">)</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE;"> </span></span>
<span class="line"><span style="--shiki-light:#999999;--shiki-dark:#666666;">}</span></span></code></pre></div>

::: details Example CSS

```css
pre.shiki .tab,
pre.shiki .space {
  position: relative;
}

pre.shiki .tab::before {
  content: '⇥';
  position: absolute;
  opacity: 0.3;
}

pre.shiki .space::before {
  content: '·';
  position: absolute;
  opacity: 0.3;
}
```

:::

---

### `transformerRenderIndentGuides`

Render indentations as individual spans, with class `indent`.

With some additional CSS rules, you can make it look like this:

<div class="language-js vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">js</span><pre v-pre class="shiki shiki-themes vitesse-light vitesse-dark" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212;" tabindex="0"><code><span class="line"><span style="color:#CB7676">function</span><span style="color:#80A665"> func</span><span style="color:#666666">()</span><span style="color:#666666"> {</span></span>
<span class="line"><span class="indent">  </span><span style="color:#BD976A">console</span><span style="color:#666666">.</span><span style="color:#80A665">log</span><span style="color:#666666">(</span><span style="color:#4C9A91">1</span><span style="color:#666666">);</span></span>
<span class="line"><span class="indent" style="--indent-offset: 0ch;"></span></span>
<span class="line"><span class="indent">  </span><span style="color:#4D9375">for</span><span style="color:#666666"> (</span><span style="color:#CB7676">const </span><span style="color:#BD976A">i</span><span style="color:#CB7676"> of</span><span style="color:#666666"> [])</span><span style="color:#666666"> {</span></span>
<span class="line"><span class="indent">  </span><span class="indent">  </span><span style="color:#BD976A">console</span><span style="color:#666666">.</span><span style="color:#80A665">log</span><span style="color:#666666">(</span><span style="color:#4C9A91">2</span><span style="color:#666666">);</span></span>
<span class="line"><span class="indent">  </span><span style="color:#666666">}</span></span>
<span class="line"><span style="color:#666666">}</span></span></code></pre></div>

::: details Example CSS

```css
pre.shiki .indent {
  display: inline-block;
  position: relative;
  left: var(--indent-offset);
}

pre.shiki .indent:empty {
  height: 1lh;
  vertical-align: bottom;
}

pre.shiki .indent::before {
  content: '';
  position: absolute;
  opacity: 0.15;
  width: 1px;
  height: 100%;
  background-color: currentColor;
}
```

:::

---

### `transformerRenderLineNumber`

Render line numbers as individual spans, with class `line-number`.

```ts
import { transformerRenderLineNumber } from '@shikijs/transformers'

const html = await codeToHtml(code, {
  transformers: [
    transformerRenderLineNumber(),
  ],
})
```

Options:

- `start`: Start number. Default `1`.
- `classLineNumber`: Class for line number. Default `'line-number'`.

With some additional CSS rules, you can make it look like this:

```css
pre.shiki .line-number {
  user-select: none;
  opacity: 0.5;
  margin-right: 1em;
}
```

---

### `transformerMetaHighlight`

Highlight lines based on the [meta string](/guide/transformers#meta) provided on the code snippet.

````md
```js {1,3-4}
console.log('1')
console.log('2')
console.log('3')
console.log('4')
```
````

Renders (with custom CSS rules):

```js {1,3-4}
console.log('1')
console.log('2')
console.log('3')
console.log('4')
```

- Outputs: `<span class="line highlighted">` for included lines.

### `transformerMetaWordHighlight`

Highlight words based on the meta string provided on the code snippet.

````md
```js /Hello/
const msg = 'Hello World'
console.log(msg)
console.log(msg) // prints Hello World
```
````

Renders (with custom CSS rules):

```js /Hello/
const msg = 'Hello World'
console.log(msg) // prints Hello World
```

Outputs: `<span class="highlighted-word">Hello</span>` for matched words.

---

### `transformerCompactLineOptions`

Support for `shiki`'s `lineOptions` that is removed in `shiki`.

---

### `transformerRemoveLineBreak`

Remove line breaks between `<span class="line">`. Useful when you set `display: block` to `.line` in CSS.

---

### `transformerRemoveNotationEscape`

Transform `// [\!code ...]` to `// [!code ...]`.
Avoid rendering the escaped notation syntax as it is.

---

### `transformerStyleToClass`

Convert Shiki's inline styles to unique classes.

Class names are generated based on the hash value of the style object with the prefix/suffix you provide. You can put this transformer in multiple highlights passes and then get the CSS at the end to reuse the exact same styles. As Shiki doesn't handle CSS, it's on your integration to decide how to extract and apply/bundle the CSS.

For example:

```ts
import { transformerStyleToClass } from '@shikijs/transformers'
import { codeToHtml } from 'shiki'

const toClass = transformerStyleToClass({ // [!code highlight:3]
  classPrefix: '__shiki_',
})

const code = `console.log('hello')`
const html = await codeToHtml(code, {
  lang: 'ts',
  themes: {
    dark: 'vitesse-dark',
    light: 'vitesse-light',
  },
  defaultColor: false,
  transformers: [toClass], // [!code highlight]
})

// The transformer instance exposes some methods to get the CSS
const css = toClass.getCSS() // [!code highlight]

// use `html` and `css` in your app
```

HTML output:

```html
<pre class="shiki shiki-themes vitesse-dark vitesse-light __shiki_9knfln" tabindex="0"><code><span class="line">
  <span class="__shiki_14cn0u">console</span>
  <span class="__shiki_ps5uht">.</span>
  <span class="__shiki_1zrdwt">log</span>
  <span class="__shiki_ps5uht">(</span>
  <span class="__shiki_236mh3">'</span>
  <span class="__shiki_1g4r39">hello</span>
  <span class="__shiki_236mh3">'</span>
  <span class="__shiki_ps5uht">)</span>
</span></code></pre>
```

CSS output:

```css
.__shiki_14cn0u {
  --shiki-dark: #bd976a;
  --shiki-light: #b07d48;
}
.__shiki_ps5uht {
  --shiki-dark: #666666;
  --shiki-light: #999999;
}
.__shiki_1zrdwt {
  --shiki-dark: #80a665;
  --shiki-light: #59873a;
}
.__shiki_236mh3 {
  --shiki-dark: #c98a7d77;
  --shiki-light: #b5695977;
}
.__shiki_1g4r39 {
  --shiki-dark: #c98a7d;
  --shiki-light: #b56959;
}
.__shiki_9knfln {
  --shiki-dark: #dbd7caee;
  --shiki-light: #393a34;
  --shiki-dark-bg: #121212;
  --shiki-light-bg: #ffffff;
}
```

### `transformerRemoveComments`

Remove comments from the code. It works by checking the internal grammar token metadata to determine if the token is a comment.

This transformer requires `includeExplanation: true` to work.

```ts
import { transformerRemoveComments } from '@shikijs/transformers'

const html = await codeToHtml(code, {
  lang: 'ts',
  includeExplanation: true, // [!code highlight]
  transformers: [
    transformerRemoveComments(), // [!code highlight]
  ],
})
```

Options:

- `removeEmptyLines`: Remove lines that become empty after removing comments. Default `true`.

For example:

````md
```js
// This is a comment
const x = 1 // Inline comment
/* Block comment */
const y = 2

// Another comment
```
````

Will renders:

```js
const x = 1
const y = 2
```

---

### Packages Twoslash (`docs/packages/twoslash.md`)

---
outline: deep
---

# @shikijs/twoslash

<Badges name="@shikijs/twoslash" />

A Shiki transformer for [Twoslash](https://github.com/twoslashes/twoslash), provide inline type hover inside code blocks.

[TwoSlash Notation Reference](https://twoslash.netlify.app/refs/notations).

## Install

::: code-group

```sh [npm]
npm install -D @shikijs/twoslash
```

```sh [yarn]
yarn add -D @shikijs/twoslash
```

```sh [pnpm]
pnpm add -D @shikijs/twoslash
```

```sh [bun]
bun add -D @shikijs/twoslash
```

```sh [deno]
deno add npm:@shikijs/twoslash
```

:::

This package is **a transformer addon** to Shiki. This means that for every integration that supports passing Shiki transformers, you can use this package.

```
/* Detailed source-code truncated for AI context efficiency. */
```

The default output is unstyled. You need to add some extra CSS to make them look good.

If you want to run Twoslash on browsers or workers, reference to the [CDN Usage](#cdn-usage) section.

## Renderers

Thanks to the flexibility of [`hast`](https://github.com/syntax-tree/hast), this transformer allows customizing how each piece of information is rendered in the output HTML with ASTs.

We provide two renderers built-in, and you can also create your own:

---

### `rendererRich`

[Source code](https://github.com/shikijs/shiki/blob/main/packages/twoslash/src/renderer-rich.ts)

::: tip
This is the default renderer since v0.10.0.
:::

This renderer provides a more explicit class name is prefixed with `twoslash-` for better scoping.
In addition, it runs syntax highlighting on the hover information.

```ts twoslash
// @twoslash-cache: {"v":1,"hash":"9bafe2d835b3a768cc39f5a28647084c02c621d41bbebd6324dead19238c244e","data":"N4Igdg9gJgpgziAXAbVAFwJ4AcZJACwgDcYAnEAGhDRgA808AKAQwBsBLZuASgAIAzAK5gAxmnYQwvUjDCwZpAErsR+RhCzjJcAPyJei2fLLLVAeU0SwPfQBUA7hDisu+Q3LJkAOmHYBbLAhSNGkjTyUVfEoQKAgRBEQQAEEpNhpSMGZxElCPBV40fCzeLFJidnk4XgAjGDR0kpl+dloYKF4RFzgqzL94Ch97dkLeOAwwNGZaXnx2AHN8DgWQ/iCC/BhedjBVgro0ADpouEngpABOKlZZOcKkAEYAJipTubq8GTyTSOiOMFxEAAGKiqZikZhiMgXAC+FHQ2ABBGIUJe+yYbE4PAEwjEVgK4Osq1IfVIDicXTUGi01j0vFsBLgRJJZOcrgAkh5aBZqTZeABlWYAa3Y9OYhKCJJ8/kCwXxYsZErILIp0Vi8TwADEIWgghhsaJqQUIB0ZFlNsx+UL2HLxcSyAI1mhHKy4PgthMYHNwTyjlQTmCGIhHgBmK43O6Ie6Xahgt6B6gMplK50qq7bAHAkCg8GQ8hB4Ow+E4PCEEjkVH0dEcLh8NCJxWklOudSWbS00W25lN10c2Bc1vWbj6AXsYUdhV20hSgJBEJ1+VJxvk1yquIJEBasS6/W4yRGk0wM28C0j4U2ickh2kArdt3bGherJWOC+kD+s6IADs9zDYFuUUjaNXneRJ507ZNl1dX50yQZ4syKHN0iQQFC2oBES2RctqDRRJGFKDQyEwPhx0XZVXG5Z8Dk+YxSHbW93Bo3gAB9eGEWBmn+KBV3VRIAGFBBOCA/FyGiqh1XhYBECpNkIexeEPVR3V2V0IEEVh2lqETPC4v1TkDc4AFZf3/JAAA4XljECQGo8JoP+JBQ3gsFtShINUMwYtElLFFsMrXCMRrLSFFMSkB10fQGPCEKKO0Ic6XosIFGnGUQhs4KfioNV1xSY9WHSTJsk2NL7UKYp8KIaTSCqWp6ntUoYGaVp2k6LgemYPo4AGMAhhGMYJimGZ5kWIaVkdDYlONGh6Ffd9A3uQEjJAa4/wjJ4LNIOMPkS75VDsgFHOzFy8yeaEAF0QWgRFpVnXhgCCnb8Aoc9SNvXhoQEMphIAcgAAVdUd2AAKzgAB6J1IPwL6fB8MCLwgl01GAHxeHu0h9GKiJVEYPgQZB3gAB4AFpCZ8aFuGiPpJiQUB9lkOArDwNAEGhaEgA"}
import { rendererRich, transformerTwoslash } from '@shikijs/twoslash'

transformerTwoslash({
  renderer: rendererRich() // <--
})
```

Here are a few examples with the built-in [`style-rich.css`](https://github.com/shikijs/shiki/blob/main/packages/twoslash/style-rich.css):



```
/* Detailed source-code truncated for AI context efficiency. */
```

```
/* Detailed source-code truncated for AI context efficiency. */
```

---

### `rendererClassic`

[Source code](https://github.com/shikijs/shiki/blob/main/packages/twoslash/src/renderer-classic.ts)

This renderer aligns with the output of legacy [`shiki-twoslash`](https://github.com/shikijs/twoslash).

```ts twoslash
// @twoslash-cache: {"v":1,"hash":"be50298f5ea6976d501abea603d4b03abe8280f3bf0a7b10df97e47172449e7c","data":"N4Igdg9gJgpgziAXAbVAFwJ4AcZJACwgDcYAnEAGhDRgA808AKAQwBsBLZuASgAIAzAK5gAxmnYQwvUjDCwZpAMKsucdiMbdEvACoB3CHBVx8AJVnyyAHTDsAtlgik00i2TLLV6yiCgQRCIggOvgwvLD8zIKsLjJy7qS8bOwA5rZgKbx67Gj4vLlhTqnsYGy8AAYAAibsANbsAFZwAPRoBkZc+OW8EIJoWH0AdD5waMzOSACcVKyyKblIAIwArFRjpCkwDEFxlkrGaiI+HGC4iAAMVCL448xiZFMAvhTo2GcExA9rdNsgLBxcPhCUTiST5UjMMBwfhOOxkfSGYz4RgQLCgqEAfm0OghUJhpDhpARHRMAEl4rQAPJoiRQrS8ADK+Dq7BxkOhsOstgcThcaFxHIJ8PaSJ8fgCeAAYnc0E4MAJhGJafkILwRDJmDQkozmfVwez8YSBE58iLOrwSjQUhD0XBhlRRuNtgAmABsMzmC0Qi2m1HGm1+/INnKJZpMxxKZ0uIGut3u5EQbuerxweEIJHI33oTGSgP1eJDxKRKJpkjgWN0AsNwsRnXJsCppbp2iZLLZBaFpBs9kcznzgsJRc6Yv8gRA0rEcoVIOVsrVGq1zB1LP71cS+NNtZMFrAVpttLtI3W2wA7AAWD0ZL0+tb+rZ4IMdwdh/AR05IZ1XG4Q+NIc7J6g3jTT5M2oH4mCwUhUTITA+HbAcaxJfBqVtQZdgSCshxMcx4gUXgAB9eGECJIygEcJSCRRBFGCA7FcXCyDgFVwhgER2FgXhCD0XgYDuPIShhXgTF6VgoF4AAjMJ0JkMiHWPKYAA5L3mV9ECUv0NnvHY3AUN8zgAZi/OMaATZ0AMwVMgnTL4wOzIJ/k4Hh6L2Tw4EOTRsRfHC9m7Hk+2kjwDm8KhxTHEIwgiKIYmchIkg4NISkybJcnyUIelIYpSlYCpql1RoWjaLcuh6PoBjQe0QEdCZvXOdTZivVSVlvTTfgC/YvCOGZIyQQyY2/GUHm9MyAF0rmgd4e15XhgBihRXMOChV0LF9eEeAQoLogByXKWSaVoX02mwbEfBDQyKxhgBsXhZrIbQ2vm9RNF4Zpml4AAeABaD6bEebgfDhMYkFAH5ZDUSQHwQR5HiAA="}
import { rendererClassic, transformerTwoslash } from '@shikijs/twoslash'

transformerTwoslash({
  renderer: rendererClassic() // <--
})
```

You might need to reference `shiki-twoslash`'s CSS to make it look good. [Here](https://github.com/shikijs/shiki/blob/main/packages/twoslash/style-classic.css) we also copied the CSS from `shiki-twoslash` but it might need some cleanup.

### `rendererFloatingVue`

[Source code](https://github.com/shikijs/shiki/blob/main/packages/vitepress-twoslash/src/renderer-floating-vue.ts)

This renderer outputs Vue template syntax that using [Floating Vue](https://floating-vue.starpad.dev/) as the popup component (to render it outside the containers). This renderer is **NOT** directly usable but an internal renderer for the [VitePress integration](/packages/vitepress#twoslash). Listing it here for reference if you want to create your own renderer.

## Options

### Explicit Trigger

When integrating with `@shikijs/markdown-it` or `rehype-shiki`, we may not want Twoslash to run on every code block. In this case, we can set `explicitTrigger` to `true` to only run on code blocks with `twoslash` presented in the codeframe.

```ts twoslash
// @twoslash-cache: {"v":1,"hash":"c33fc7b243f56e1595f93ca6e9e052fed95abb58a9edc5376b448c62984626f1","data":"N4Igdg9gJgpgziAXAbVAFwJ4AcZJACwgDcYAnEAGhDRgA808AKAQwBsBLZuASgAIAzAK5gAxmnYQwvNKWZg4/CKQC2ZACoB3CHFZd8jCFnGS4AfkS81s+YpXqtOvQEkwsWgHkjE+dwsBlfHYAa3YrOQUlVVIAHTB2ZSwlNGlrCLtSTW1dOHxKECgIEQREEAAxZjElDAFhMW9pCF4RUhhmGl5mXgDg9hTw2yiBJWkHbPxedjAaAHNZY3kAOjy4NGZSBkQATipWGDBptFzEAEZt6jXpmA3qVIH7LL08jjBcRAAGKhF8NYqaci2AL4UdDYV4EYhkPI0ehMNicHh9GyRe6OHIGLwmcyWW7IjKjZyuOieeY8fyBEJhJHpWLxRLrRFpKKZVG5KgFIp4cqVUjVISieYNJotNowDpdcm9GT9XFDUgjB45CZTGCzNreOBLKgrNYbACsAHYdnsDkdTlRVqRLtcpVSmfick9Jq8AEyfb6yMSQ95AkE4PCEEjkc10a6MLCkQxkTB8SmMlFjYnqhZ0LAcETsNBWdjTS6kLEAIwgEF2cl4AB9eAAlFUAUVoWHLvGEsH4TqgeXZxRA1YAjoJ2C04LwAAZoe34YeC/Oi8PwPY0KBK6T4UUiaCi/OsQpBXiqVaC5hYVPVQ7sIc2uNB87TYrIZD5GD8ZiCVgMKhP1hwXAAXW/WotGwABzHEa+yHEgxy6uaFxXHgKZphmWY5pCOxOkgADMbo/J6/zOgCf4gGusB4LSSS8MADJ3HiCrjACAgRsovAAOQAAI5D0ABWcAAPRjjRTGxLEF5UcyYyMMAsS8Lw8HsOmmakNmuYWDIgiitx3G8AAPAAtNpsQAtweR7swSCgCGexwN4eBoAgAIAkAA="}
import { transformerTwoslash } from '@shikijs/twoslash'

transformerTwoslash({
  explicitTrigger: true // <--
})
```

````md
In markdown, you can use the following syntax to trigger Twoslash:

```ts
// this is a normal code block
```

```ts twoslash
// this will run Twoslash
```
````

## Integrations

While you can set up Twoslash with Shiki on your own with the instructions above, you can also find high-level integrations with frameworks and tools here:

- [VitePress](/packages/vitepress#twoslash) - A plugin to enable Twoslash support in VitePress.
- [Nuxt](/packages/nuxt#twoslash-integration) - A module to enable Twoslash for Nuxt Content.
- [Vocs](https://vocs.dev/docs/guides/twoslash) - Vocs has TwoSlash support built-in.
- [Slidev](https://sli.dev/features/twoslash#twoslash-integration) - Slidev has TwoSlash support built-in.

## Recipes

### CDN Usage

By default [`twoslash`](https://github.com/twoslashes/twoslash/tree/main/packages/twoslash) runs on Node.js and relies on your local system to resolve TypeScript and types for the imports. Import it directly in non-Node.js environments would not work.

Luckily, Twoslash implemented a virtual file system, which allow you to provide your own files for TypeScript to resolve in memory. However, loading these files in the browser is still a challenge. Thanks to the work on the [TypeScript WebSite](https://github.com/microsoft/TypeScript-Website), the TypeScript team has provided some utilities to fetch types on demand through CDN, they call it [Automatic Type Acquisition (ATA)](https://github.com/microsoft/TypeScript-Website/tree/v2/packages/ata).

We make tiny wrappers around the building blocks and provide an easy-to-use API in [`twoslash-cdn`](https://twoslash.netlify.app/packages/cdn). For example:

```js
// FIXME: Replace with explicit versions in production
import { createTransformerFactory, rendererRich } from 'https://esm.sh/@shikijs/twoslash@latest/core'
import { codeToHtml } from 'https://esm.sh/shiki@latest'
import { createTwoslashFromCDN } from 'https://esm.sh/twoslash-cdn@latest'
import { createStorage } from 'https://esm.sh/unstorage@latest'
import indexedDbDriver from 'https://esm.sh/unstorage@latest/drivers/indexedb'

// ============= Initialization =============

// An example using unstorage with IndexedDB to cache the virtual file system
const storage = createStorage({
  driver: indexedDbDriver({ base: 'twoslash-cdn' }),
})

const twoslash = createTwoslashFromCDN({
  storage,
  compilerOptions: {
    lib: ['esnext', 'dom'],
  },
})

const transformerTwoslash = createTransformerFactory(twoslash.runSync)({
  renderer: rendererRich(),
})

// ============= Execution =============

const app = document.getElementById('app')

const source = `
import { ref } from 'vue'

console.log("Hi! Shiki + Twoslash on CDN :)")

const count = ref(0)
//     ^?
`.trim()

// Before rendering, we need to prepare the types, so that the rendering can happen synchronously
await twoslash.prepareTypes(source)

// Then we can render the code
app.innerHTML = await codeToHtml(source, {
  lang: 'ts',
  theme: 'vitesse-dark',
  transformers: [transformerTwoslash],
})
```

---

### Packages Vitepress (`docs/packages/vitepress.md`)

---
outline: deep
---

# VitePress Integration

[VitePress](https://vitepress.dev/) uses Shiki under the hood, so you don't need explicit integration.

VitePress provides [a few options for customizing Shiki](https://github.com/vuejs/vitepress/blob/main/src/node/markdown/markdown.ts#L66-L112). Learn more about them in the [VitePress documentation](https://vitepress.dev/reference/site-config#markdown).

## Twoslash

To enable [TypeScript Twoslash](/packages/twoslash) (type hover on code snippets) in VitePress, we provide a VitePress plugin for easy setup. Pre-styled, with [Floating Vue](https://floating-vue.starpad.dev/) to display the type information out side of the code container.

<Badges name="@shikijs/vitepress-twoslash" />

### Setup

::: code-group

```sh [npm]
npm i -D @shikijs/vitepress-twoslash
```

```sh [yarn]
yarn add -D @shikijs/vitepress-twoslash
```

```sh [pnpm]
pnpm add -D @shikijs/vitepress-twoslash
```

```sh [bun]
bun add -D @shikijs/vitepress-twoslash
```

```sh [deno]
deno add npm:@shikijs/vitepress-twoslash
```

:::

In your [`.vitepress/config.ts`](https://vitepress.dev/reference/site-config):

```ts [.vitepress/config.ts]
import { transformerTwoslash } from '@shikijs/vitepress-twoslash' // [!code hl]
import { defineConfig } from 'vitepress'

export default defineConfig({
  markdown: {
    codeTransformers: [
      transformerTwoslash() // [!code hl]
    ],
    // [!code hl:2]
    // Explicitly load these languages for types highlighting
    languages: ['js', 'jsx', 'ts', 'tsx']
  }
})
```

And then in your [`.vitepress/theme/index.ts`](https://vitepress.dev/guide/custom-theme), install the Vue plugin and import the css with `@shikijs/vitepress-twoslash/styles.css`.

```
/* Detailed source-code truncated for AI context efficiency. */
```

::: details About style.css

For easier setup, `@shikijs/vitepress-twoslash/styles.css` bundles the styles from `floating-vue` and `@shikijs/twoslash/style-rich.css` so you only need a single entry. If you are using a custom `floating-vue` style or want to have more control of the styles, you can expand them as:

```ts
import '@shikijs/vitepress-twoslash/style.css'

// Equivalent to:
import '@shikijs/twoslash/style-rich.css'
import 'floating-vue/dist/style.css'
import '@shikijs/vitepress-twoslash/style-core.css'
```

:::

That's it, you can now use `ts twoslash` in your markdown files to enable the beautiful type hover.

````md
```ts twoslash
console.log('hello')
//      ^?
```
````

It will be rendered as:

```
/* Detailed source-code truncated for AI context efficiency. */
```

<div class="h-100" /> 

### Vue Single File Component

In addition, this plugin also integrated [`twoslash-vue`](https://twoslash.netlify.app/packages/vue) for you, so that you can also highlight Vue SFC blocks with `vue twoslash`:

```
/* Detailed source-code truncated for AI context efficiency. */
```

### File System Cache

To speed up the build process, you can enable the file system cache for the generated types, that shares across multiple builds. By default the cache is stored in the `.vitepress/cache/twoslash` along with other VitePress caches.

In your [`.vitepress/config.ts`](https://vitepress.dev/reference/site-config):

```ts [.vitepress/config.ts]
import { transformerTwoslash } from '@shikijs/vitepress-twoslash'
import { createFileSystemTypesCache } from '@shikijs/vitepress-twoslash/cache-fs' // [!code hl]
import { defineConfig } from 'vitepress'

export default defineConfig({
  markdown: {
    codeTransformers: [
      transformerTwoslash({
        typesCache: createFileSystemTypesCache() // [!code hl]
      })
    ]
  }
})
```

### Inline Cache (experimental)

You can enable inline caching for the generated types. The `@twoslash-cache: ...` will auto insert into your fenced code block during development and build time.

````md [./your-file.md]{2}
```ts twoslash
// @twoslash-cache: [auto generated]
const a: string = 'foo'
```
````

To enable the inline cache in your [`.vitepress/config.ts`](https://vitepress.dev/reference/site-config), please use the config wrapper created by `createTwoslashWithInlineCache`.

```ts [.vitepress/config.ts]
import { createTwoslashWithInlineCache } from '@shikijs/vitepress-twoslash/cache-inline' // [!code hl]
import { defineConfig } from 'vitepress'

const withTwoslashInlineCache = createTwoslashWithInlineCache({ // [!code hl]
  // ... config of transformerTwoslash // [!code hl]
}) // [!code hl]

export default withTwoslashInlineCache( // [!code hl]
  defineConfig({
    markdown: {
      codeTransformers: [
        // move config to the `createTwoslashWithInlineCache()` // [!code error]
        // transformerTwoslash({ ... }) // [!code error]
      ]
    }
  })
) // [!code hl]
```

#### Force re-generate inline cache

To force re-generate the inline cache and ignore the existing cache, you can use the `TWOSLASH_INLINE_CACHE_IGNORE` environment variable when running the `vitepress` cli.

```bash
TWOSLASH_INLINE_CACHE_IGNORE=1 vitepress dev
TWOSLASH_INLINE_CACHE_IGNORE=1 vitepress build
```

#### Remove inline cache

To remove all inline cache, you can use the `TWOSLASH_INLINE_CACHE_REMOVE` environment variable when running the `vitepress` cli.

```bash
TWOSLASH_INLINE_CACHE_REMOVE=1 vitepress dev
TWOSLASH_INLINE_CACHE_REMOVE=1 vitepress build
```

---

### References Engine Js Compat (`docs/references/engine-js-compat.md`)

# JavaScript RegExp Engine Compatibility References

Compatibility reference of all built-in grammars with the [JavaScript RegExp engine](/guide/regex-engines#javascript-regexp-engine).

> Generated on Friday, September 11, 2026
>
> Version `4.4.3`
>
> Runtime: Node.js v24.16.0

## Report Summary

|                 |                       Count |
| :-------------- | --------------------------: |
| Total Languages |                         238 |
| Supported       | [237](#supported-languages) |
| Mismatched      |  [0](#mismatched-languages) |
| Unsupported     | [1](#unsupported-languages) |

## Supported Languages

Languages that work with the JavaScript RegExp engine, and will produce the same result as the WASM engine (with the [sample snippets in the registry](https://github.com/shikijs/textmate-grammars-themes/tree/main/samples)).
In some edge cases, it's not guaranteed that the highlighting will be 100% the same. If that happens, please create an issue with the sample snippet.

| Language           | Highlight Match | Patterns Parsable | Patterns Failed | Diff |
| ------------------ | :-------------- | ----------------: | --------------: | ---: |
| abap               | ✅ OK           |                52 |               - |      |
| actionscript-3     | ✅ OK           |                59 |               - |      |
| ada                | ✅ OK           |               200 |               - |      |
| ahk                | ✅ OK           |               125 |               - |      |
| angular-html       | ✅ OK           |               669 |               - |      |
| angular-ts         | ✅ OK           |               776 |               - |      |
| apache             | ✅ OK           |                60 |               - |      |
| apex               | ✅ OK           |               197 |               - |      |
| apl                | ✅ OK           |               912 |               - |      |
| applescript        | ✅ OK           |               152 |               - |      |
| ara                | ✅ OK           |                54 |               - |      |
| asciidoc           | ✅ OK           |               319 |               - |      |
| asm                | ✅ OK           |               298 |               - |      |
| astro              | ✅ OK           |               610 |               - |      |
| awk                | ✅ OK           |                36 |               - |      |
| ballerina          | ✅ OK           |               224 |               - |      |
| bat                | ✅ OK           |                58 |               - |      |
| beancount          | ✅ OK           |                42 |               - |      |
| berry              | ✅ OK           |                21 |               - |      |
| bibtex             | ✅ OK           |                19 |               - |      |
| bicep              | ✅ OK           |                31 |               - |      |
| bird2              | ✅ OK           |               146 |               - |      |
| blade              | ✅ OK           |              1122 |               - |      |
| bsl                | ✅ OK           |                97 |               - |      |
| c                  | ✅ OK           |               176 |               - |      |
| c3                 | ✅ OK           |               125 |               - |      |
| cadence            | ✅ OK           |               128 |               - |      |
| cairo              | ✅ OK           |               236 |               - |      |
| chapel             | ✅ OK           |               214 |               - |      |
| clarity            | ✅ OK           |                42 |               - |      |
| clojure            | ✅ OK           |                38 |               - |      |
| cmake              | ✅ OK           |                23 |               - |      |
| cobol              | ✅ OK           |               858 |               - |      |
| codeowners         | ✅ OK           |                 4 |               - |      |
| codeql             | ✅ OK           |               147 |               - |      |
| coffee             | ✅ OK           |               466 |               - |      |
| common-lisp        | ✅ OK           |                60 |               - |      |
| coq                | ✅ OK           |                28 |               - |      |
| cpp                | ✅ OK           |               468 |               - |      |
| crystal            | ✅ OK           |              1054 |               - |      |
| csharp             | ✅ OK           |               315 |               - |      |
| css                | ✅ OK           |               141 |               - |      |
| csv                | ✅ OK           |                 1 |               - |      |
| cue                | ✅ OK           |                85 |               - |      |
| cypher             | ✅ OK           |                39 |               - |      |
| d                  | ✅ OK           |               272 |               - |      |
| dart               | ✅ OK           |                79 |               - |      |
| dax                | ✅ OK           |                23 |               - |      |
| desktop            | ✅ OK           |                16 |               - |      |
| diff               | ✅ OK           |                16 |               - |      |
| docker             | ✅ OK           |                 7 |               - |      |
| dotenv             | ✅ OK           |                 9 |               - |      |
| dream-maker        | ✅ OK           |                56 |               - |      |
| edge               | ✅ OK           |               629 |               - |      |
| elixir             | ✅ OK           |               704 |               - |      |
| elm                | ✅ OK           |               241 |               - |      |
| emacs-lisp         | ✅ OK           |               155 |               - |   22 |
| erb                | ✅ OK           |              1816 |               - |      |
| erlang             | ✅ OK           |               270 |               - |      |
| fennel             | ✅ OK           |                31 |               - |      |
| fish               | ✅ OK           |                34 |               - |      |
| fluent             | ✅ OK           |                23 |               - |      |
| fortran-fixed-form | ✅ OK           |               346 |               - |      |
| fortran-free-form  | ✅ OK           |               339 |               - |      |
| fsharp             | ✅ OK           |               242 |               - |      |
| gdresource         | ✅ OK           |               157 |               - |      |
| gdscript           | ✅ OK           |                94 |               - |      |
| gdshader           | ✅ OK           |                39 |               - |      |
| genie              | ✅ OK           |                20 |               - |      |
| gherkin            | ✅ OK           |                19 |               - |      |
| gleam              | ✅ OK           |                26 |               - |      |
| glimmer-js         | ✅ OK           |               673 |               - |      |
| glimmer-ts         | ✅ OK           |               673 |               - |      |
| glsl               | ✅ OK           |               185 |               - |      |
| gn                 | ✅ OK           |                19 |               - |      |
| gnuplot            | ✅ OK           |                82 |               - |      |
| go                 | ✅ OK           |               126 |               - |      |
| graphql            | ✅ OK           |               445 |               - |      |
| groovy             | ✅ OK           |               130 |               - |      |
| hack               | ✅ OK           |               946 |               - |      |
| haml               | ✅ OK           |               559 |               - |      |
| handlebars         | ✅ OK           |               696 |               - |      |
| haskell            | ✅ OK           |               157 |               - |      |
| haxe               | ✅ OK           |               173 |               - |      |
| hcl                | ✅ OK           |                67 |               - |      |
| hjson              | ✅ OK           |                57 |               - |      |
| hlsl               | ✅ OK           |                52 |               - |      |
| html               | ✅ OK           |               608 |               - |      |
| http               | ✅ OK           |               746 |               - |      |
| hurl               | ✅ OK           |               611 |               - |      |
| hxml               | ✅ OK           |               182 |               - |      |
| hy                 | ✅ OK           |                12 |               - |      |
| imba               | ✅ OK           |               239 |               - |      |
| ini                | ✅ OK           |                11 |               - |      |
| java               | ✅ OK           |               141 |               - |      |
| javascript         | ✅ OK           |               375 |               - |      |
| jinja              | ✅ OK           |               639 |               - |      |
| jison              | ✅ OK           |               431 |               - |      |
| json               | ✅ OK           |                19 |               - |      |
| json5              | ✅ OK           |                23 |               - |      |
| jsonc              | ✅ OK           |                19 |               - |      |
| jsonl              | ✅ OK           |                19 |               - |      |
| jsonnet            | ✅ OK           |                33 |               - |      |
| jssm               | ✅ OK           |                30 |               - |      |
| jsx                | ✅ OK           |               375 |               - |      |
| julia              | ✅ OK           |              1177 |               - |      |
| just               | ✅ OK           |              2148 |               - |      |
| kdl                | ✅ OK           |                30 |               - |      |
| kotlin             | ✅ OK           |                58 |               - |      |
| kusto              | ✅ OK           |                60 |               - |      |
| latex              | ✅ OK           |               312 |               - |      |
| lean               | ✅ OK           |                31 |               - |      |
| less               | ✅ OK           |               279 |               - |      |
| liquid             | ✅ OK           |               689 |               - |      |
| llvm               | ✅ OK           |                25 |               - |      |
| log                | ✅ OK           |                31 |               - |      |
| logo               | ✅ OK           |                 9 |               - |      |
| lua                | ✅ OK           |               279 |               - |      |
| luau               | ✅ OK           |                95 |               - |      |
| make               | ✅ OK           |                51 |               - |      |
| markdown           | ✅ OK           |               123 |               - |      |
| marko              | ✅ OK           |               929 |               - |      |
| matlab             | ✅ OK           |                88 |               - |      |
| mdc                | ✅ OK           |               786 |               - |      |
| mdx                | ✅ OK           |               197 |               - |      |
| mermaid            | ✅ OK           |               175 |               - |      |
| mipsasm            | ✅ OK           |                17 |               - |      |
| mojo               | ✅ OK           |               215 |               - |      |
| moonbit            | ✅ OK           |               101 |               - |      |
| move               | ✅ OK           |               113 |               - |      |
| narrat             | ✅ OK           |                34 |               - |      |
| nextflow           | ✅ OK           |               107 |               - |      |
| nginx              | ✅ OK           |               378 |               - |      |
| nim                | ✅ OK           |              1122 |               - |      |
| nix                | ✅ OK           |                83 |               - |      |
| nsis               | ✅ OK           |                38 |               - |      |
| nushell            | ✅ OK           |                85 |               - |      |
| objective-c        | ✅ OK           |               222 |               - |      |
| objective-cpp      | ✅ OK           |               306 |               - |      |
| ocaml              | ✅ OK           |               178 |               - |      |
| odin               | ✅ OK           |                98 |               - |      |
| openscad           | ✅ OK           |                24 |               - |      |
| org                | ✅ OK           |              4715 |               - |      |
| pascal             | ✅ OK           |                23 |               - |      |
| perl               | ✅ OK           |               937 |               - |      |
| php                | ✅ OK           |              1125 |               - |      |
| pkl                | ✅ OK           |                67 |               - |      |
| plsql              | ✅ OK           |                45 |               - |      |
| po                 | ✅ OK           |                23 |               - |      |
| polar              | ✅ OK           |                31 |               - |      |
| postcss            | ✅ OK           |                49 |               - |      |
| powerquery         | ✅ OK           |                30 |               - |      |
| powershell         | ✅ OK           |                88 |               - |      |
| prisma             | ✅ OK           |                31 |               - |      |
| prolog             | ✅ OK           |                26 |               - |      |
| proto              | ✅ OK           |                33 |               - |      |
| pug                | ✅ OK           |               683 |               - |      |
| puppet             | ✅ OK           |                60 |               - |      |
| purescript         | ✅ OK           |                87 |               - |      |
| python             | ✅ OK           |               218 |               - |      |
| qml                | ✅ OK           |               407 |               - |      |
| qmldir             | ✅ OK           |                 7 |               - |      |
| qss                | ✅ OK           |                31 |               - |      |
| r                  | ✅ OK           |                66 |               - |      |
| racket             | ✅ OK           |                69 |               - |      |
| raku               | ✅ OK           |                52 |               - |      |
| razor              | ✅ OK           |               960 |               - |      |
| rbs                | ✅ OK           |                33 |               - |      |
| reg                | ✅ OK           |                 9 |               - |      |
| regexp             | ✅ OK           |                34 |               - |      |
| rel                | ✅ OK           |                17 |               - |      |
| riscv              | ✅ OK           |                36 |               - |      |
| ron                | ✅ OK           |                35 |               - |      |
| rosmsg             | ✅ OK           |                31 |               - |      |
| rst                | ✅ OK           |              2057 |               - |      |
| ruby               | ✅ OK           |              1808 |               - |      |
| rust               | ✅ OK           |                89 |               - |      |
| sas                | ✅ OK           |               100 |               - |      |
| sass               | ✅ OK           |                69 |               - |      |
| scala              | ✅ OK           |               116 |               - |      |
| scheme             | ✅ OK           |                34 |               - |      |
| scss               | ✅ OK           |               233 |               - |      |
| sdbl               | ✅ OK           |                23 |               - |      |
| shaderlab          | ✅ OK           |                87 |               - |      |
| shellscript        | ✅ OK           |               147 |               - |      |
| shellsession       | ✅ OK           |               149 |               - |      |
| smalltalk          | ✅ OK           |                64 |               - |      |
| smithy             | ✅ OK           |                42 |               - |      |
| solidity           | ✅ OK           |               105 |               - |      |
| soy                | ✅ OK           |               646 |               - |      |
| sparql             | ✅ OK           |                19 |               - |      |
| splunk             | ✅ OK           |                17 |               - |      |
| sql                | ✅ OK           |                68 |               - |      |
| ssh-config         | ✅ OK           |                12 |               - |      |
| stata              | ✅ OK           |               254 |               - |      |
| stylus             | ✅ OK           |               106 |               - |      |
| surrealql          | ✅ OK           |               467 |               - |      |
| svelte             | ✅ OK           |               646 |               - |      |
| swift              | ✅ OK           |               340 |               - |      |
| system-verilog     | ✅ OK           |               102 |               - |      |
| systemd            | ✅ OK           |                32 |               - |      |
| talonscript        | ✅ OK           |                46 |               - |      |
| tasl               | ✅ OK           |                23 |               - |      |
| tcl                | ✅ OK           |                34 |               - |      |
| templ              | ✅ OK           |               674 |               - |      |
| terraform          | ✅ OK           |                68 |               - |      |
| tex                | ✅ OK           |               103 |               - |      |
| toml               | ✅ OK           |                44 |               - |      |
| ts-tags            | ✅ OK           |               997 |               - |      |
| tsv                | ✅ OK           |                 1 |               - |      |
| tsx                | ✅ OK           |               375 |               - |      |
| turtle             | ✅ OK           |                15 |               - |      |
| twig               | ✅ OK           |              2445 |               - |      |
| typescript         | ✅ OK           |               362 |               - |      |
| typespec           | ✅ OK           |                73 |               - |      |
| typst              | ✅ OK           |              5699 |               - |      |
| v                  | ✅ OK           |                77 |               - |      |
| vala               | ✅ OK           |                20 |               - |      |
| vb                 | ✅ OK           |                34 |               - |      |
| verilog            | ✅ OK           |                34 |               - |      |
| vhdl               | ✅ OK           |                82 |               - |      |
| viml               | ✅ OK           |                72 |               - |      |
| vue                | ✅ OK           |               705 |               - |      |
| vue-html           | ✅ OK           |               404 |               - |      |
| vue-vine           | ✅ OK           |              1032 |               - |      |
| vyper              | ✅ OK           |               238 |               - |      |
| wasm               | ✅ OK           |                78 |               - |      |
| wenyan             | ✅ OK           |                18 |               - |      |
| wgsl               | ✅ OK           |                44 |               - |      |
| wikitext           | ✅ OK           |               105 |               - |      |
| wit                | ✅ OK           |                81 |               - |      |
| wolfram            | ✅ OK           |               501 |               - |      |
| xml                | ✅ OK           |               168 |               - |      |
| xsl                | ✅ OK           |               170 |               - |      |
| yaml               | ✅ OK           |                48 |               - |      |
| zenscript          | ✅ OK           |                21 |               - |      |
| zig                | ✅ OK           |                51 |               - |      |

###### Table Field Explanations

- **Highlight Match**: Whether the highlighting results matched with the WASM engine for the [sample snippet](https://github.com/shikijs/textmate-grammars-themes/tree/main/samples).
- **Patterns Parsable**: Number of regex patterns that can be parsed by the JavaScript RegExp engine.
- **Patterns Failed**: Number of regex patterns that can't be parsed by the JavaScript RegExp engine (throws error).
- **Diff**: Length of characters that are different between the highlighting results of the two engines.

## Mismatched Languages

Languages that do not throw with the JavaScript RegExp engine, but will produce different results than the WASM engine.

| Language | Highlight Match | Patterns Parsable | Patterns Failed | Diff |
| -------- | :-------------- | ----------------: | --------------: | ---: |

## Unsupported Languages

Languages that throw with the JavaScript RegExp engine, either because they contain syntax we can't polyfill yet or because the grammar contains an invalid Oniguruma regex (that would also fail when using the WASM engine, but silently). You can try these languages with the `forgiving` option to skip errors.

| Language | Highlight Match | Patterns Parsable | Patterns Failed | Diff |
| -------- | :-------------- | ----------------: | --------------: | ---: |
| ahk2     | ❌ Error        |               188 |               1 |   16 |

---

--- METRICS ---
- Files Extracted: 62
- Estimated Token Budget: ~43211 tokens
- Canonical Reference: https://codewiki.google/github.com/shikijs/shiki
