RegExp Engines
TextMate grammars are based on regular expressions that match tokens. More specifically, they assume that 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.
Since v1.15, we expose the ability for users to switch the regex engine or provide a custom implementation. To do so, add an engine
option to createHighlighter
or createHighlighterCore
. For example:
import { createHighlighter } from 'shiki'
const shiki = await createShiki({
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.
import { createHighlighter } from 'shiki'
import { createOnigurumaEngine } from 'shiki/engine/oniguruma'
const shiki = await createShiki({
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, they might contain syntax that is not supported by JavaScript's RegExp
, or expect different behavior for the same syntax. So we use Oniguruma-To-ES to transpile Oniguruma patterns to native JavaScript regexes.
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 advantages of using the JavaScript engine are that it doesn't require loading a large WebAssembly file for Oniguruma and it is faster for some grammars (since the regular expressions run as native JavaScript).
Please check the compatibility table for the support status of languages you are using.
The JavaScript engine is strict by default, and will throw an error if it encounters a pattern that it cannot convert. If mismatches are acceptable and you want best-effort results for unsupported grammars, you can enable the forgiving
option to suppress any conversion errors:
const jsEngine = createJavaScriptRegexEngine({ forgiving: true })
// ...use the engine
INFO
If you run Shiki on Node.js (or at build time) and bundle size or WebAssembly support is not a concern, we still recommend using the Oniguruma engine.
The JavaScript engine is best when running in the browser and in cases when you want to control the bundle size.
JavaScript Runtime Target
For best results, the JavaScript engine uses the RegExp v
flag, which is available in Node.js v20+ and ES2024 (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:
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.
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.
You can install them with @shikijs/langs-precompiled
, and then change your @shikijs/langs
imports to @shikijs/langs-precompiled
:
import { createHighlighterCore } from 'shiki/core'
import { createJavaScriptRawEngine } from 'shiki/engine/javascript'
const highlighter = await createHighlighterCore({
langs: [
import('@shikijs/langs/javascript'),
import('@shikijs/langs/typescript'),
import('@shikijs/langs-precompiled/javascript'),
import('@shikijs/langs-precompiled/typescript'),
// ...
],
themes: [
import('@shikijs/themes/nord'),
],
engine: createJavaScriptRegexEngine(),
engine: createJavaScriptRawEngine(),
})
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
, you can generate pre-compiled languages with the --precompiled
and --engine=javascript-raw
flags.