Skip to content

Configuring Expressive Code

Expressive Code comes with default settings that should work out of the box for most sites. However, you can customize the configuration to match your needs by providing an options object to Expressive Code.

How to define your configuration

There are two ways to configure Expressive Code:

  • You can pass your options to the initialization function of the integration you are using
  • In Astro or Starlight projects, you can also create a file named ec.config.mjs in your project’s root directory and export your configuration object from there instead

Passing options directly

The default method to configure Expressive Code is to pass your options to the initialization function of the integration you are using:

astro.config.mjs
import { defineConfig } from 'astro/config'
import astroExpressiveCode from 'astro-expressive-code'
export default defineConfig({
integrations: [
astroExpressiveCode({
// You can set configuration options here
themes: ['dracula', 'github-light'],
styleOverrides: {
// You can also override styles
borderRadius: '0.5rem',
frames: {
shadowColor: '#124',
},
},
}),
],
})

Using an ec.config.mjs file

In projects based on Astro or Starlight, you also have the option to create a file named ec.config.mjs in your project’s root directory and export your configuration object as the default export from there:

ec.config.mjs
import { defineEcConfig } from 'astro-expressive-code'
export default defineEcConfig({
// You can set configuration options here
themes: ['dracula', 'github-light'],
styleOverrides: {
// You can also override styles
borderRadius: '0.5rem',
frames: {
shadowColor: '#124',
},
},
})

Core options

The following options are supported by the core package and all integrations:

cascadeLayer

Type: string Default: undefined

Allows to specify a CSS cascade layer name that should be used for all generated CSS.

If you are using cascade layers on your site to control the order in which CSS rules are applied, set this option to a non-empty string, and Expressive Code will wrap all of its generated CSS styles in a @layer rule with the given name.

customizeTheme

Type: (theme) => void | ExpressiveCodeTheme Default: undefined

This optional function is called once per theme during engine initialization with the loaded theme as its only argument.

It allows customizing the loaded theme and can be used for various purposes: - You can change a theme’s name property to influence the CSS needed to select it (e.g., when using the default settings for themeCssRoot and themeCssSelector, setting theme.name = 'dark' will allow theme selection using <html data-theme="dark">). - You can create color variations of themes by using theme.applyHueAndChromaAdjustments().

You can optionally return an ExpressiveCodeTheme instance from this function to replace the theme provided in the configuration. This allows you to create a copy of the theme and modify it without affecting the original instance.

Parameters

defaultLocale

Type: string Default: 'en-US'

The locale that should be used for text content.

defaultProps

Type: Partial<ExpressiveCodeBlockProps> & { overridesByLang: Object } Default: undefined

An optional set of default props for all code blocks in your project.

For example, setting this to { wrap: true } enables word wrapping on all code blocks by default, saving you from having to manually set this option on every single code block.

Object properties

overridesByLang
Type: Record<string, Partial<ExpressiveCodeBlockProps>>

Allows to override the default props based on a code block’s syntax highlighting language.

Use the language IDs as keys and an object containing the props as values. The keys also support specifying multiple language IDs separated by commas to apply the same props to multiple languages.

Example

defaultProps: {
wrap: true,
overridesByLang: {
'bash,sh,zsh': { wrap: false }
}
}

logger

Allows to customize how Expressive Code logs messages. The default implementation logs all messages to the console and prepends them with the label expressive-code like this:

[expressive-code] This is a debug message.

Available logger options

  • label: string
    The label to use as a prefix for all log messages. Defaults to 'expressive-code'.
  • debug: (message: string) => void
    A function to log a debug message.
  • info: (message: string) => void
    A function that logs an info message.
  • warn: (message: string) => void
    A function that logs a warning message.
  • error: (message: string) => void
    A function that logs an error message.

minSyntaxHighlightingColorContrast

Type: number Default: 5.5

Determines if Expressive Code should process the syntax highlighting colors of all themes to ensure an accessible minimum contrast ratio between foreground and background colors.

The default of 5.5 ensures a contrast ratio of at least 5.5:1. You can change the desired contrast ratio by providing another value, or turn the feature off by setting this option to 0.

plugins

Type: (ExpressiveCodePlugin | ExpressiveCodePlugin[])[] Default: []

An optional array of plugins that should be used when rendering code blocks.

To add a plugin, import its initialization function and call it inside this array.

If the plugin has any configuration options, you can pass them to the initialization function as an object containing your desired property values.

If any nested arrays are found inside the plugins array, they will be flattened before processing.

styleOverrides

Type: StyleOverrides Default: {}

An optional set of style overrides that can be used to customize the appearance of the rendered code blocks without having to write custom CSS.

See the Style Overrides page for more details.

theme

themeCssRoot

Type: string Default: ':root'

Allows to customize the base selector used to scope theme-dependent CSS styles.

The default selector ':root' ensures that all required CSS variables are globally available.

themeCssSelector

Type: false | ((theme, context) => string | false) Default: (theme) => `[data-theme='${theme.name}']`

Allows to customize the selectors used to manually switch between multiple themes.

These selectors are useful if you want to allow your users to choose a theme instead of relying solely on the media query generated by useDarkModeMediaQuery.

You can add a theme selector either to your <html> element (which is targeted by the themeCssRoot default value of :root), and/or any individual code block wrapper.

For example, when using the default settings, selecting the theme github-light for the entire page would look like this:

<html data-theme="github-light">

If your site’s theme switcher requires a different approach, you can customize the selectors using this option. For example, if you want to use class names instead of a data attribute, you could set this option to a function that returns .theme-${theme.name} instead.

If you want to prevent the generation of theme-specific CSS rules altogether, you can set this to false or return it from the function.

themes

The color themes that should be available for your code blocks.

CSS variables will be generated for all themes, allowing to select the theme to display using CSS. If you specify one dark and one light theme, a prefers-color-scheme media query will also be generated by default. You can customize this to match your site’s needs through the useDarkModeMediaQuery and themeCssSelector options.

Defaults to the github-dark and github-light themes.

useDarkModeMediaQuery

Type: boolean

Determines if CSS code is generated that uses a prefers-color-scheme media query to automatically switch between light and dark themes based on the user’s system preferences.

Defaults to true if your themes option is set to one dark and one light theme (which is the default), and false otherwise.

useStyleReset

Type: boolean Default: true

Determines if code blocks should be protected against influence from site-wide styles.

Defaults to true, which causes Expressive Code to use the declaration all: revert to revert all CSS properties to the values they would have had without any site-wide styles. This ensures the most predictable results out of the box.

You can set this to false if you want your site-wide styles to influence the code blocks.

useThemedScrollbars

Type: boolean Default: true

Whether the themes are allowed to style the scrollbars.

If set to false, scrollbars will be rendered using the browser’s default style.

Note that you can override the individual scrollbar colors defined by the theme using the styleOverrides option.

useThemedSelectionColors

Type: boolean Default: false

Whether the themes are allowed to style selected text.

By default, Expressive Code renders selected text in code blocks using the browser’s default style to maximize accessibility. If you want your selections to be more colorful, you can set this option to true to allow using theme selection colors instead.

Note that you can override the individual selection colors defined by the theme using the styleOverrides option.

Options of expressive-code

The expressive-code package bundles the core package with all default plugin packages. To allow controlling the plugins, it also supports the following options in addition to the ones above:

frames

Type: boolean | PluginFramesOptions Default: true

The Frames plugin adds an editor or terminal frame around code blocks, including an optional title displayed as a tab or window caption.

This plugin is enabled by default. Set this to false to disable it. You can also configure the plugin by setting this to an options object.

shiki

Type: boolean | PluginShikiOptions Default: true

The Shiki plugin adds syntax highlighting to code blocks.

This plugin is enabled by default. Set this to false to disable it. You can also configure the plugin by setting this to an options object.

textMarkers

Type: boolean Default: true

The Text Markers plugin allows to highlight lines and inline ranges in code blocks in various styles (e.g. marked, inserted, deleted).

This plugin is enabled by default. Set this to false to disable it.

Options of rehype-expressive-code

In addition to the options above, the rehype integration also supports the following options:

customCreateBlock

Type: ({ input, file }) => ExpressiveCodeBlock | Promise<ExpressiveCodeBlock> Default: undefined

This optional function allows you to customize how ExpressiveCodeBlock instances are created from code blocks found in the Markdown document.

The function is expected to return an ExpressiveCodeBlock instance or a promise resolving to one.

Parameters

  • input: ExpressiveCodeBlockOptions
    Block data for the ExpressiveCodeBlock constructor.

  • file: VFileWithOutput<null>
    A VFile instance representing the Markdown document.

customCreateRenderer

Type: (options) => Promise<RehypeExpressiveCodeRenderer> | RehypeExpressiveCodeRenderer Default: undefined

This advanced option allows you to influence the rendering process by creating your own ExpressiveCode instance or processing the base styles and JS modules added to every page.

The return value will be cached and used for all code blocks in the document.

Parameters

  • options: RehypeExpressiveCodeOptions

getBlockLocale

Type: ({ input, file }) => string | undefined | Promise<string | undefined> Default: undefined

This optional function provides support for multi-language sites by allowing you to customize the locale used for a given code block.

If the function returns undefined, the default locale provided in the Expressive Code configuration is used.

Parameters

  • input: ExpressiveCodeBlockOptions
    Block data for the ExpressiveCodeBlock constructor.

  • file: VFileWithOutput<null>
    A VFile instance representing the Markdown document.

tabWidth

Type: number Default: 2

The number of spaces that should be used to render tabs.

Any tabs found in code blocks in your markdown/MDX documents will be replaced with the specified number of spaces. This ensures that the code blocks are rendered consistently across browsers and platforms.

If you want to preserve tabs in your code blocks, set this option to 0.

themes

Type: ThemeObjectOrShikiThemeName[] Default: ['github-dark', 'github-light']

The color themes that should be available for your code blocks.

CSS variables will be generated for all themes, allowing to select the theme to display using CSS. If you specify one dark and one light theme, a prefers-color-scheme media query will also be generated by default. You can customize this to match your site’s needs through the useDarkModeMediaQuery and themeCssSelector options.

The following item types are supported in this array:

  • any theme name bundled with Shiki (e.g. dracula)
  • any theme object compatible with VS Code or Shiki (e.g. imported from an NPM theme package)
  • any ExpressiveCodeTheme instance (e.g. using ExpressiveCodeTheme.fromJSONString(...) to load a custom JSON/JSONC theme file yourself)

Options of astro-expressive-code

In addition to the options above, the Astro integration also supports the following options:

emitExternalStylesheet

Type: boolean Default: true

Determines if the styles required to display code blocks should be emitted into a separate CSS file rather than being inlined into the rendered HTML of the first code block per page.

This is recommended for sites containing multiple pages with code blocks, as it will reduce the overall footprint of the site when navigating between pages.

The generated URL is located inside Astro’s assets directory and includes a content hash so it can be cached indefinitely by browsers. If you are using the default values for the Astro config options base, build.assets, build.assetsPrefix, the resulting URL will be /_astro/ec.{hash}.css.

Options of the Starlight integration

In addition to the options above, the Starlight integration provides two themes (starlight-dark and starlight-light) and options (useStarlightDarkModeSwitch and useStarlightUiThemeColors) that are specific to Starlight.

Please refer to the Starlight Configuration Reference for more details.

Referenced types

ExpressiveCodeBlockProps

Properties

preserveIndent
Type: boolean Default: true

If true, wrapped parts of long lines will be aligned with their line’s indentation level, making the wrapped code appear to start at the same column. This increases readability of the wrapped code and can be especially useful for languages where indentation is significant, e.g. Python.

If false, wrapped parts of long lines will always start at column 1. This can be useful to reproduce terminal output.

wrap
Type: boolean Default: false

If true, word wrapping will be enabled for the code block, causing lines that exceed the available width to wrap to the next line. You can use the preserveIndent option to control how wrapped lines are indented.

If false, lines that exceed the available width will cause a horizontal scrollbar to appear.