Skip to main content
The @streamdown/code package provides syntax highlighting powered by Shiki. It implements the CodeHighlighterPlugin interface and integrates with the plugins.code field on <Streamdown>.

Installation

npm install @streamdown/code

Quick start

import { createCodePlugin } from "@streamdown/code";
import { Streamdown } from "streamdown";

const plugins = {
  code: createCodePlugin({
    themes: ["github-light", "github-dark"],
  }),
};

<Streamdown plugins={plugins}>
  {markdown}
</Streamdown>

createCodePlugin()

Creates a CodeHighlighterPlugin instance with optional configuration.
function createCodePlugin(options?: CodePluginOptions): CodeHighlighterPlugin
options
CodePluginOptions
Optional configuration for the plugin.
A pre-configured instance with default settings is also exported:
import { code } from "@streamdown/code";

CodeHighlighterPlugin interface

type
code-highlighter
required
Discriminant for the plugin union type. Always "code-highlighter".
name
shiki
required
Plugin identifier. Always "shiki".
highlight
(options: HighlightOptions, callback?: (result: HighlightResult) => void) => HighlightResult | null
required
Highlights code and returns a token result. Returns null synchronously when the Shiki highlighter is still loading; the optional callback is called with the result once highlighting completes. Subsequent calls with the same input return the cached result immediately.
getSupportedLanguages
() => BundledLanguage[]
required
Returns the full list of language identifiers supported by the bundled Shiki distribution.
getThemes
() => [ThemeInput, ThemeInput]
required
Returns the [lightTheme, darkTheme] tuple that was provided at construction time (or the defaults). Streamdown reads this value to synchronise the theme tuple across the component tree.
supportsLanguage
(language: BundledLanguage) => boolean
required
Returns true if language (after alias normalisation) is in the set of bundled Shiki languages. Language aliases such as "js" for "javascript" are resolved automatically.

HighlightOptions

Passed as the first argument to highlight().
code
string
required
The source code string to highlight.
language
BundledLanguage
required
The language identifier. Aliases (e.g. "js", "py") are resolved automatically.
themes
[ThemeInput, ThemeInput]
required
The [lightTheme, darkTheme] tuple to use for this highlight call. Typically sourced from getThemes() or the shikiTheme prop on <Streamdown>.

HighlightResult

The value returned by a successful highlight() call or delivered to the callback. Compatible with Shiki’s TokensResult.
tokens
HighlightToken[][]
required
A two-dimensional array of tokens. The outer array represents lines; the inner array represents individual tokens on each line.
bg
string
Background colour for the code block (CSS colour string), derived from the active theme.
fg
string
Default foreground colour for the code block (CSS colour string), derived from the active theme.
rootStyle
string | false
Inline styles to apply to the root element of the highlighted output, as a CSS string. false when no root styles are needed.

HighlightToken

A single token within a highlighted line.
content
string
required
The text content of this token.
color
string
Foreground colour for this token as a CSS colour string.
bgColor
string
Background colour for this token as a CSS colour string.
htmlAttrs
Record<string, string>
Additional HTML attributes to apply to the token’s <span> element.
htmlStyle
Record<string, string>
Additional inline styles to apply to the token’s <span> element.
offset
number
Character offset of this token from the start of the line.

ThemeInput type

type ThemeInput = BundledTheme | ThemeRegistrationAny;
BundledTheme is a string union of all themes shipped with Shiki (e.g. "github-light", "nord", "dracula"). ThemeRegistrationAny accepts a custom theme object loaded via the Shiki theme registration API.

Caching behaviour

The plugin maintains a module-level token cache keyed on language + themes + code. The first call for a given combination triggers async highlighter initialisation. Subsequent calls with the same key return the cached result synchronously without re-highlighting.
Highlighter instances are also cached per (language, lightTheme, darkTheme) combination. This means the first call for a new language may take a moment to load the grammar, but all subsequent calls are instant.

Build docs developers (and LLMs) love