Built-in

Syntax Highlighting

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

The comark/plugins/highlight plugin provides syntax highlighting for code blocks using Shiki. It supports multiple themes, line highlighting, and on-demand language loading.

shiki is a peer dependency, install it alongside Comark:

terminal
npm install shiki

Usage

Pass bundled theme names as strings — no extra imports needed:

import { parse } from 'comark'
import highlight from 'comark/plugins/highlight'

const result = await parse(content, {
  plugins: [
    highlight({
      themes: {
        light: 'github-light',
        dark: 'github-dark'
      }
    })
  ]
})

With framework components:

<script setup lang="ts">
import { Comark } from '@comark/vue'
import highlight from '@comark/vue/plugins/highlight'

const plugins = [
  highlight({
    themes: { light: 'github-light', dark: 'github-dark' }
  })
]
</script>

<template>
  <Suspense>
    <Comark :plugins="plugins">{{ content }}</Comark>
  </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 →

highlight({
  themes: {
    light: 'github-light',
    dark: 'github-dark'
  }
})

You can also pass theme registration objects (for example from @shikijs/themes) when you want explicit control over what gets bundled:

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

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

Language Detection

Comark reads the language from the code fence info string and highlights accordingly. Languages are loaded on demand by default. 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

Import languages from @shikijs/langs to preload them and gain type safety:

import javascript from '@shikijs/langs/javascript'
import typescript from '@shikijs/langs/typescript'
import python from '@shikijs/langs/python'

highlight({
  languages: [javascript, typescript, python]
})
Without explicit languages, unregistered languages are loaded on demand. Use registerDefaultLanguages: false with an explicit languages array for the smallest bundle.

Transformers

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

import { transformerNotationDiff } from '@shikijs/transformers'

highlight({
  themes: { light: 'github-light', dark: 'github-dark' },
  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

highlight(options?)

Returns a ComarkPlugin that enables Shiki syntax highlighting.

Parameters:

  • options? - Optional configuration, see Options

Returns: ComarkPlugin


Options

OptionTypeDefaultDescription
themesobjectMaterial themesLight and dark theme names or registrations
languagesLanguageRegistration[]undefinedLanguages to preload
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

themes

Theme configuration for light and dark modes. Each value can be a bundled theme name string (resolved from shiki) or a theme registration object.

highlight({
  themes: {
    light: 'github-light',
    dark: 'github-dark'
  }
})

Default: { light: 'material-theme-lighter', dark: 'material-theme-palenight' }

languages

Languages to preload. Import from @shikijs/langs. Without this option, languages are loaded on demand.

import javascript from '@shikijs/langs/javascript'
import typescript from '@shikijs/langs/typescript'

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

Default: undefined

transformers

An array of Shiki transformers applied to every highlighted block.

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

highlight({
  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.

highlight({ preStyles: true })

Default: false

registerDefaultLanguages

When true, these languages are pre-registered: vue, tsx, svelte, astro, typescript, javascript, mdc, bash, json, yaml. Set to false to control the language set entirely via languages.

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

Default: true

registerDefaultThemes

When true, material-theme-lighter (light) and material-theme-palenight (dark) are pre-registered. Set to false when using only custom themes.

highlight({
  registerDefaultThemes: false,
  themes: { light: 'github-light', dark: 'github-dark' }
})

Default: true


Examples

GitHub Theme

import { parse } from 'comark'
import highlight from 'comark/plugins/highlight'

const result = await parse(content, {
  plugins: [highlight({ themes: { light: 'github-light', dark: 'github-dark' } })]
})

Minimal Bundle

Disable defaults and import only what you need. Prefer theme registration objects here so unused bundled themes are tree-shaken:

import javascript from '@shikijs/langs/javascript'
import typescript from '@shikijs/langs/typescript'
import githubDark from '@shikijs/themes/github-dark'

highlight({
  registerDefaultLanguages: false,
  registerDefaultThemes: false,
  languages: [javascript, typescript],
  themes: { dark: githubDark }
})

With Transformers

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

highlight({
  themes: { light: 'github-light', dark: 'github-dark' },
  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.

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>