Shiki (Highlight)

Plugin for syntax highlighting code blocks using Shiki with multi-theme support.

The comark/plugins/shiki plugin provides syntax highlighting for code blocks using Shiki. It supports multiple themes, line highlighting, and dual light/dark palettes.

comark/plugins/highlight is a deprecated alias of comark/plugins/shiki and will be removed in the next major version. Prefer import shiki from 'comark/plugins/shiki'.

For a lighter alternative (no TextMate grammars), see comark/plugins/rangi.

Installation

shiki is a peer dependency — install it alongside Comark:

terminal
npm install shiki

For explicit theme/language imports (recommended for tree-shaking and the core entry), also install the standalone packages:

terminal
npm install @shikijs/themes @shikijs/langs
PackageWhen you need it
shikiAlways — peer dependency of the plugin
@shikijs/themesImporting themes like @shikijs/themes/github-dark
@shikijs/langsImporting languages like @shikijs/langs/typescript
The standard entry ships Material themes + a default language set, so you can call shiki() with no options after installing only shiki. Install @shikijs/themes / @shikijs/langs when you pass custom themes / languages, or when you use comark/plugins/shiki/core.

Usage

Standard entry

Zero-config — defaults cover Material light/dark and common languages (vue, tsx, svelte, astro, typescript, javascript, bash, json, yaml, plus Comark/mdc):

import { parseMarkdown } from 'comark'
import shiki from 'comark/plugins/shiki'

const result = await parseMarkdown(content, {
  plugins: [shiki()]
})

Override themes (import from @shikijs/themes):

import { parseMarkdown } from 'comark'
import shiki from 'comark/plugins/shiki'
import githubLight from '@shikijs/themes/github-light'
import githubDark from '@shikijs/themes/github-dark'

const result = await parseMarkdown(content, {
  plugins: [
    shiki({
      themes: {
        light: githubLight,
        dark: githubDark
      }
    })
  ]
})

Core entry (minimal bundle)

To keep default theme/language chunks out of the bundle entirely, use core. themes and languages are required (ShikiCoreOptions) — there are no registerDefault* flags:

import shiki from 'comark/plugins/shiki/core'
import javascript from '@shikijs/langs/javascript'
import typescript from '@shikijs/langs/typescript'
import githubLight from '@shikijs/themes/github-light'
import githubDark from '@shikijs/themes/github-dark'

const plugins = [
  shiki({
    languages: [javascript, typescript],
    themes: { light: githubLight, dark: githubDark },
  })
]

Framework packages expose the same nested entry, for example @comark/vue/plugins/shiki/core and @comark/react/plugins/shiki/core.

The Comark TextMate grammar can also be imported independently from the plugin-owned language entry:

import comarkLanguages, { comarkLanguage } from 'comark/plugins/shiki/language-comark'

The default export includes the Comark grammar and its Markdown, YAML, and HTML dependencies, ready to pass to Shiki. This entry does not import rangi.

With framework components:

<script setup lang="ts">
import { Markdown } from '@comark/vue'
import shiki from '@comark/vue/plugins/shiki'
import githubLight from '@shikijs/themes/github-light'
import githubDark from '@shikijs/themes/github-dark'

const plugins = [
  shiki({
    themes: { light: githubLight, dark: githubDark }
  })
]
</script>

<template>
  <Suspense>
    <Markdown :plugins="plugins">{{ content }}</Markdown>
  </Suspense>
</template>

<style scoped>
html.dark .shiki :deep(span) {
  color: var(--shiki-dark) !important;
  background-color: var(--shiki-dark-bg) !important;
  font-style: var(--shiki-dark-font-style) !important;
  font-weight: var(--shiki-dark-font-weight) !important;
  text-decoration: var(--shiki-dark-text-decoration) !important;
}
</style>

Features

Dual-Theme Support

Highlight code with different themes for light and dark modes. Both palettes are embedded as CSS custom properties, so there is no flash on theme switch. See all available themes →

shiki({
  themes: {
    light: githubLight,
    dark: githubDark
  }
})

Language Detection

Comark reads the language from the code fence info string and highlights accordingly. On the standard entry, the default language set is pre-registered; pass extra grammars via languages (from @shikijs/langs). On core, only the languages you pass are available. See all 180+ supported languages →

```typescript
const x: number = 42
```

Line Highlighting

Highlight specific lines using {line-numbers} syntax:

```javascript {2-3,5}
function example() {
  const a = 1  // highlighted
  const b = 2  // highlighted
  const c = 3
  return a + b + c  // highlighted
}
```

Lines receive the .highlight class; see Styling for the required CSS.

Filename Metadata

Display a filename label above the code block:

```javascript [server.js]
const app = express()
```

Language Loading

Install @shikijs/langs and import grammars to register extra languages (or all languages on core):

terminal
npm install @shikijs/langs
import javascript from '@shikijs/langs/javascript'
import typescript from '@shikijs/langs/typescript'
import python from '@shikijs/langs/python'

shiki({
  languages: [javascript, typescript, python]
})
Standard: default languages are pre-registered; languages merges on top. Use registerDefaultLanguages: false to replace the set entirely. Core: no defaults — languages is required and is the full set (plus the built-in Comark/mdc grammar).

Transformers

Pass any Shiki transformer via transformers to add diff annotations, focus lines, or custom classes:

import { transformerNotationDiff } from '@shikijs/transformers'

shiki({
  themes: { light: githubLight, dark: githubDark },
  transformers: [transformerNotationDiff()]
})

The most powerful transformer is @shikijs/twoslash: it runs the TypeScript compiler on your code blocks to add inline type tooltips and error annotations.

Pre Styles

Set preStyles: true to add inline background and foreground colors to <pre> elements based on the active theme.


API

shiki(options?) — standard entry

import shiki from 'comark/plugins/shiki'
// shiki(options?: ShikiOptions): ComarkPlugin

Returns a ComarkPlugin with bundled Material themes and the default language set. Options are optional.

shiki(options) — core entry

import shiki from 'comark/plugins/shiki/core'
// shiki(options: ShikiCoreOptions): ComarkPlugin

Returns a ComarkPlugin with no bundled themes or languages. themes and languages are required. Import them from @shikijs/themes and @shikijs/langs.


Options

Two option types, one per entry:

  • ShikiOptionscomark/plugins/shiki (standard). Themes/languages optional; includes registerDefaultThemes / registerDefaultLanguages.
  • ShikiCoreOptionscomark/plugins/shiki/core. themes and languages are required; no registerDefault* options (nothing is bundled by default).

Standard (ShikiOptions)

OptionTypeDefaultDescription
themesobjectMaterial themesLight and dark theme registrations
languagesLanguageRegistration[]undefinedExtra languages (merged onto the default set)
transformersShikiTransformer[]undefinedShiki transformers applied to every block
preStylesbooleanfalseAdd inline background/foreground styles to <pre>
registerDefaultLanguagesbooleantrueRegister the built-in default language set
registerDefaultThemesbooleantrueRegister the built-in Material themes

Core (ShikiCoreOptions)

OptionTypeDefaultDescription
themesobjectrequiredLight and/or dark theme registrations
languagesLanguageRegistration[]requiredLanguages to register
transformersShikiTransformer[]undefinedShiki transformers applied to every block
preStylesbooleanfalseAdd inline background/foreground styles to <pre>

themes

Theme configuration for light and dark modes. Install @shikijs/themes and import from there:

terminal
npm install @shikijs/themes
import githubLight from '@shikijs/themes/github-light'
import githubDark from '@shikijs/themes/github-dark'

shiki({
  themes: {
    light: githubLight,
    dark: githubDark
  }
})

Standard default: { light: materialThemeLighter, dark: materialThemePalenight } (when registerDefaultThemes is true). Core: required (at least one of light / dark).

languages

Languages to register. Install @shikijs/langs and import from there. On the standard entry, values are merged on top of the default set when registerDefaultLanguages is true.

terminal
npm install @shikijs/langs
import javascript from '@shikijs/langs/javascript'
import typescript from '@shikijs/langs/typescript'

shiki({
  languages: [javascript, typescript]
})

Standard default: undefined (default set still registered via registerDefaultLanguages). Core: required.

transformers

An array of Shiki transformers applied to every highlighted block.

import { transformerNotationDiff, transformerNotationHighlight } from '@shikijs/transformers'

shiki({
  transformers: [
    transformerNotationDiff(),       // [!code ++] / [!code --]
    transformerNotationHighlight(),  // [!code highlight]
  ]
})

Default: undefined

preStyles

Add inline background and foreground color styles to <pre> elements based on the active theme.

shiki({ preStyles: true })

Default: false

registerDefaultLanguages

Standard entry only. When true, these languages are pre-registered: vue, tsx, svelte, astro, typescript, javascript, bash, json, yaml (plus the built-in Comark/mdc grammar). Set to false to control the language set entirely via languages.

shiki({
  registerDefaultLanguages: false,
  languages: [javascript, typescript]
})

Default: true

registerDefaultThemes

Standard entry only. When true, registers material-theme-lighter (light) and material-theme-palenight (dark). Set it to false to skip loading those themes at runtime. To keep their import chunks out of a consumer bundle entirely, use comark/plugins/shiki/core instead.

shiki({
  registerDefaultThemes: false,
  themes: { light: githubLight, dark: githubDark }
})

Default: true


Examples

GitHub Theme

import { parseMarkdown } from 'comark'
import shiki from 'comark/plugins/shiki'
import githubLight from '@shikijs/themes/github-light'
import githubDark from '@shikijs/themes/github-dark'

const result = await parseMarkdown(content, {
  plugins: [shiki({ themes: { light: githubLight, dark: githubDark } })]
})

Minimal Bundle

Install the standalone Shiki packages, use the core entry (ShikiCoreOptions), and import only what you need — languages and themes are required, and no defaults are bundled:

terminal
npm install shiki @shikijs/langs @shikijs/themes
import shiki from 'comark/plugins/shiki/core'
import javascript from '@shikijs/langs/javascript'
import typescript from '@shikijs/langs/typescript'
import githubDark from '@shikijs/themes/github-dark'

shiki({
  languages: [javascript, typescript],
  themes: { dark: githubDark }
})

With Transformers

import {
  transformerNotationDiff,
  transformerNotationHighlight,
  transformerNotationFocus,
} from '@shikijs/transformers'

shiki({
  themes: { light: githubLight, dark: githubDark },
  transformers: [
    transformerNotationDiff(),       // [!code ++] / [!code --]
    transformerNotationHighlight(),  // [!code highlight]
    transformerNotationFocus(),      // [!code focus]
  ]
})

See the Twoslash guide for TypeScript-powered type tooltips and error annotations in code blocks.

Need a copy button or collapse threshold on a custom ProsePre? After highlighting there is no code prop — reconstruct the source with __node and textContent().

Live Examples

Vue + Vite Highlight

Dual-theme support, 10+ languages, theme toggle. Includes JavaScript, TypeScript, Python, Rust, Go, SQL and more.

Vue + Vite Twoslash

Browser-side twoslash with CDN-fetched TypeScript types and interactive type popups.

Styling

Shiki outputs tokens as <span class="line"> elements inside a <pre class="shiki"> block.

Line Highlight

Lines set with {1,3-5} syntax receive the .highlight class:

.shiki span.line.highlight {
  background-color: rgba(255, 255, 0, 0.1);
  display: inline-block;
  width: calc(100% + 2rem);
  margin: 0 -1rem;
  padding: 0 1rem;
}

Dark Mode

When both light and dark themes are provided, Shiki embeds both palettes as CSS custom properties on every <span>. Activate the dark palette based on your project's dark-mode class:

html.dark .shiki span {
  color: var(--shiki-dark) !important;
  background-color: var(--shiki-dark-bg) !important;
  font-style: var(--shiki-dark-font-style) !important;
  font-weight: var(--shiki-dark-font-weight) !important;
  text-decoration: var(--shiki-dark-text-decoration) !important;
}

In Vue scoped styles, use :deep() to reach Shiki spans:

<style scoped>
html.dark .shiki :deep(span) {
  color: var(--shiki-dark) !important;
  background-color: var(--shiki-dark-bg) !important;
  font-style: var(--shiki-dark-font-style) !important;
  font-weight: var(--shiki-dark-font-weight) !important;
  text-decoration: var(--shiki-dark-text-decoration) !important;
}
</style>