Themes
Expressive Code supports the full range of themes available for VS Code. Any theme that works in the editor will work in Expressive Code!
You can use our bundled themes, import themes from NPM, or export themes from VS Code.
Key features
- Efficient multi-theme support: You can use multiple themes at the same time, e.g. a dark and a light theme for improved accessibility. In contrast to other popular solutions, this does not lead to duplicate HTML output with one block per theme. Instead, Expressive Code renders every code block once and uses optimized CSS variables to allow switching between themes.
- Dark mode support: If you specify one dark and one light theme, Expressive Code automatically displays the theme that matches the user’s system preferences. You can configure this with the
useDarkModeMediaQuery
option. - Theme switcher support: If your site has a theme switcher that allows users to pick a theme, Expressive Code can generate CSS code to support it. By default, you can use the
data-theme
attribute to select a theme by name, both on thehtml
element and individual code blocks. You can configure this with thethemeCssRoot
andthemeCssSelector
options. - Automatic color contrast correction: Many themes you’ll find online do not meet the WCAG color contrast requirements for accessibility. Expressive Code fixes this by automatically processing the syntax highlighting colors of any theme to ensure a color contrast ratio of at least 5.5:1. You can configure this with the
minSyntaxHighlightingColorContrast
option.
Using bundled themes
Expressive Code comes with a set of popular themes bundled with our default syntax highlighter. You can use them by passing their names to the themes
option:
import { defineConfig } from 'astro/config'import astroExpressiveCode from 'astro-expressive-code'
export default defineConfig({ integrations: [ astroExpressiveCode({ // Replace the default themes with a custom set of bundled themes: // "dracula" (a dark theme) and "solarized-light" themes: ['dracula', 'solarized-light'], }), ],})
import { defineConfig } from 'astro/config'import starlight from '@astrojs/starlight'
export default defineConfig({ integrations: [ starlight({ title: 'My Starlight site', expressiveCode: { // Replace the default themes with a custom set of bundled themes: // "dracula" (a dark theme) and "solarized-light" themes: ['dracula', 'solarized-light'], }, }), ],})
import createMDX from '@next/mdx'import rehypeExpressiveCode from 'rehype-expressive-code'
/** @type {import('rehype-expressive-code').RehypeExpressiveCodeOptions} */const rehypeExpressiveCodeOptions = { // Replace the default themes with a custom set of bundled themes: // "dracula" (a dark theme) and "solarized-light" themes: ['dracula', 'solarized-light'],}
/** @type {import('next').NextConfig} */const nextConfig = { reactStrictMode: true, pageExtensions: ["js", "jsx", "ts", "tsx", "md", "mdx"],}
const withMDX = createMDX({ extension: /\.mdx?$/, options: { remarkPlugins: [], rehypePlugins: [ // The nested array structure is required to pass options // to a rehype plugin [rehypeExpressiveCode, rehypeExpressiveCodeOptions], ], },})
export default withMDX(nextConfig)
Available themes
function isEven(num) { return num % 2 === 0;}
// Testing the functionconst num = 4;if (isEven(num)) { console.log(`${num} is even.`);} else { throw new Error('Wait, what?');}
function isEven(num) { return num % 2 === 0;}
// Testing the functionconst num = 4;if (isEven(num)) { console.log(`${num} is even.`);} else { throw new Error('Wait, what?');}
function isEven(num) { return num % 2 === 0;}
// Testing the functionconst num = 4;if (isEven(num)) { console.log(`${num} is even.`);} else { throw new Error('Wait, what?');}
function isEven(num) { return num % 2 === 0;}
// Testing the functionconst num = 4;if (isEven(num)) { console.log(`${num} is even.`);} else { throw new Error('Wait, what?');}
function isEven(num) { return num % 2 === 0;}
// Testing the functionconst num = 4;if (isEven(num)) { console.log(`${num} is even.`);} else { throw new Error('Wait, what?');}
function isEven(num) { return num % 2 === 0;}
// Testing the functionconst num = 4;if (isEven(num)) { console.log(`${num} is even.`);} else { throw new Error('Wait, what?');}
function isEven(num) { return num % 2 === 0;}
// Testing the functionconst num = 4;if (isEven(num)) { console.log(`${num} is even.`);} else { throw new Error('Wait, what?');}
function isEven(num) { return num % 2 === 0;}
// Testing the functionconst num = 4;if (isEven(num)) { console.log(`${num} is even.`);} else { throw new Error('Wait, what?');}
function isEven(num) { return num % 2 === 0;}
// Testing the functionconst num = 4;if (isEven(num)) { console.log(`${num} is even.`);} else { throw new Error('Wait, what?');}
function isEven(num) { return num % 2 === 0;}
// Testing the functionconst num = 4;if (isEven(num)) { console.log(`${num} is even.`);} else { throw new Error('Wait, what?');}
function isEven(num) { return num % 2 === 0;}
// Testing the functionconst num = 4;if (isEven(num)) { console.log(`${num} is even.`);} else { throw new Error('Wait, what?');}
function isEven(num) { return num % 2 === 0;}
// Testing the functionconst num = 4;if (isEven(num)) { console.log(`${num} is even.`);} else { throw new Error('Wait, what?');}
function isEven(num) { return num % 2 === 0;}
// Testing the functionconst num = 4;if (isEven(num)) { console.log(`${num} is even.`);} else { throw new Error('Wait, what?');}
function isEven(num) { return num % 2 === 0;}
// Testing the functionconst num = 4;if (isEven(num)) { console.log(`${num} is even.`);} else { throw new Error('Wait, what?');}
function isEven(num) { return num % 2 === 0;}
// Testing the functionconst num = 4;if (isEven(num)) { console.log(`${num} is even.`);} else { throw new Error('Wait, what?');}
function isEven(num) { return num % 2 === 0;}
// Testing the functionconst num = 4;if (isEven(num)) { console.log(`${num} is even.`);} else { throw new Error('Wait, what?');}
function isEven(num) { return num % 2 === 0;}
// Testing the functionconst num = 4;if (isEven(num)) { console.log(`${num} is even.`);} else { throw new Error('Wait, what?');}
function isEven(num) { return num % 2 === 0;}
// Testing the functionconst num = 4;if (isEven(num)) { console.log(`${num} is even.`);} else { throw new Error('Wait, what?');}
function isEven(num) { return num % 2 === 0;}
// Testing the functionconst num = 4;if (isEven(num)) { console.log(`${num} is even.`);} else { throw new Error('Wait, what?');}
function isEven(num) { return num % 2 === 0;}
// Testing the functionconst num = 4;if (isEven(num)) { console.log(`${num} is even.`);} else { throw new Error('Wait, what?');}
function isEven(num) { return num % 2 === 0;}
// Testing the functionconst num = 4;if (isEven(num)) { console.log(`${num} is even.`);} else { throw new Error('Wait, what?');}
function isEven(num) { return num % 2 === 0;}
// Testing the functionconst num = 4;if (isEven(num)) { console.log(`${num} is even.`);} else { throw new Error('Wait, what?');}
function isEven(num) { return num % 2 === 0;}
// Testing the functionconst num = 4;if (isEven(num)) { console.log(`${num} is even.`);} else { throw new Error('Wait, what?');}
function isEven(num) { return num % 2 === 0;}
// Testing the functionconst num = 4;if (isEven(num)) { console.log(`${num} is even.`);} else { throw new Error('Wait, what?');}
function isEven(num) { return num % 2 === 0;}
// Testing the functionconst num = 4;if (isEven(num)) { console.log(`${num} is even.`);} else { throw new Error('Wait, what?');}
function isEven(num) { return num % 2 === 0;}
// Testing the functionconst num = 4;if (isEven(num)) { console.log(`${num} is even.`);} else { throw new Error('Wait, what?');}
function isEven(num) { return num % 2 === 0;}
// Testing the functionconst num = 4;if (isEven(num)) { console.log(`${num} is even.`);} else { throw new Error('Wait, what?');}
function isEven(num) { return num % 2 === 0;}
// Testing the functionconst num = 4;if (isEven(num)) { console.log(`${num} is even.`);} else { throw new Error('Wait, what?');}
function isEven(num) { return num % 2 === 0;}
// Testing the functionconst num = 4;if (isEven(num)) { console.log(`${num} is even.`);} else { throw new Error('Wait, what?');}
function isEven(num) { return num % 2 === 0;}
// Testing the functionconst num = 4;if (isEven(num)) { console.log(`${num} is even.`);} else { throw new Error('Wait, what?');}
function isEven(num) { return num % 2 === 0;}
// Testing the functionconst num = 4;if (isEven(num)) { console.log(`${num} is even.`);} else { throw new Error('Wait, what?');}
function isEven(num) { return num % 2 === 0;}
// Testing the functionconst num = 4;if (isEven(num)) { console.log(`${num} is even.`);} else { throw new Error('Wait, what?');}
function isEven(num) { return num % 2 === 0;}
// Testing the functionconst num = 4;if (isEven(num)) { console.log(`${num} is even.`);} else { throw new Error('Wait, what?');}
function isEven(num) { return num % 2 === 0;}
// Testing the functionconst num = 4;if (isEven(num)) { console.log(`${num} is even.`);} else { throw new Error('Wait, what?');}
function isEven(num) { return num % 2 === 0;}
// Testing the functionconst num = 4;if (isEven(num)) { console.log(`${num} is even.`);} else { throw new Error('Wait, what?');}
function isEven(num) { return num % 2 === 0;}
// Testing the functionconst num = 4;if (isEven(num)) { console.log(`${num} is even.`);} else { throw new Error('Wait, what?');}
function isEven(num) { return num % 2 === 0;}
// Testing the functionconst num = 4;if (isEven(num)) { console.log(`${num} is even.`);} else { throw new Error('Wait, what?');}
function isEven(num) { return num % 2 === 0;}
// Testing the functionconst num = 4;if (isEven(num)) { console.log(`${num} is even.`);} else { throw new Error('Wait, what?');}
function isEven(num) { return num % 2 === 0;}
// Testing the functionconst num = 4;if (isEven(num)) { console.log(`${num} is even.`);} else { throw new Error('Wait, what?');}
function isEven(num) { return num % 2 === 0;}
// Testing the functionconst num = 4;if (isEven(num)) { console.log(`${num} is even.`);} else { throw new Error('Wait, what?');}
function isEven(num) { return num % 2 === 0;}
// Testing the functionconst num = 4;if (isEven(num)) { console.log(`${num} is even.`);} else { throw new Error('Wait, what?');}
function isEven(num) { return num % 2 === 0;}
// Testing the functionconst num = 4;if (isEven(num)) { console.log(`${num} is even.`);} else { throw new Error('Wait, what?');}
function isEven(num) { return num % 2 === 0;}
// Testing the functionconst num = 4;if (isEven(num)) { console.log(`${num} is even.`);} else { throw new Error('Wait, what?');}
function isEven(num) { return num % 2 === 0;}
// Testing the functionconst num = 4;if (isEven(num)) { console.log(`${num} is even.`);} else { throw new Error('Wait, what?');}
function isEven(num) { return num % 2 === 0;}
// Testing the functionconst num = 4;if (isEven(num)) { console.log(`${num} is even.`);} else { throw new Error('Wait, what?');}
function isEven(num) { return num % 2 === 0;}
// Testing the functionconst num = 4;if (isEven(num)) { console.log(`${num} is even.`);} else { throw new Error('Wait, what?');}
function isEven(num) { return num % 2 === 0;}
// Testing the functionconst num = 4;if (isEven(num)) { console.log(`${num} is even.`);} else { throw new Error('Wait, what?');}
function isEven(num) { return num % 2 === 0;}
// Testing the functionconst num = 4;if (isEven(num)) { console.log(`${num} is even.`);} else { throw new Error('Wait, what?');}
function isEven(num) { return num % 2 === 0;}
// Testing the functionconst num = 4;if (isEven(num)) { console.log(`${num} is even.`);} else { throw new Error('Wait, what?');}
function isEven(num) { return num % 2 === 0;}
// Testing the functionconst num = 4;if (isEven(num)) { console.log(`${num} is even.`);} else { throw new Error('Wait, what?');}
function isEven(num) { return num % 2 === 0;}
// Testing the functionconst num = 4;if (isEven(num)) { console.log(`${num} is even.`);} else { throw new Error('Wait, what?');}
function isEven(num) { return num % 2 === 0;}
// Testing the functionconst num = 4;if (isEven(num)) { console.log(`${num} is even.`);} else { throw new Error('Wait, what?');}
function isEven(num) { return num % 2 === 0;}
// Testing the functionconst num = 4;if (isEven(num)) { console.log(`${num} is even.`);} else { throw new Error('Wait, what?');}
function isEven(num) { return num % 2 === 0;}
// Testing the functionconst num = 4;if (isEven(num)) { console.log(`${num} is even.`);} else { throw new Error('Wait, what?');}
Configuration
removeUnusedThemes
In Astro and Starlight, Expressive Code automatically removes any themes from the full Shiki bundle that are not used by your configuration. This reduces the SSR bundle size by over 1 MB. You can control this behavior with the removeUnusedThemes
option.
If you really need to disable this automatic optimization because of an advanced use case that requires access all bundled Shiki themes, you can set this option to false
.
import { defineConfig } from 'astro/config'import astroExpressiveCode from 'astro-expressive-code'import cloudflare from '@astrojs/cloudflare'
export default defineConfig({ integrations: [ astroExpressiveCode({ // This example uses non-default themes, but unused theme removal // also works with the default themes ['github-dark', 'github-light'] themes: ['catppuccin-macchiato', 'catppuccin-latte'], // Disable automatic removal of unused themes from the bundle, // allowing access to other themes than the ones specified // in the `themes` option removeUnusedThemes: false, }), ], // This example uses server-side rendering (SSR) // with the Cloudflare adapter output: 'server', adapter: cloudflare(),})
import { defineConfig } from 'astro/config'import starlight from '@astrojs/starlight'import cloudflare from '@astrojs/cloudflare'
export default defineConfig({ integrations: [ starlight({ title: 'My Starlight site', expressiveCode: { // This example uses non-default themes, but unused theme removal // also works with the default themes ['github-dark', 'github-light'] themes: ['catppuccin-macchiato', 'catppuccin-latte'], // Disable automatic removal of unused themes from the bundle, // allowing access to other themes than the ones specified // in the `themes` option removeUnusedThemes: false, }, }), ], // This example uses server-side rendering (SSR) // with the Cloudflare adapter output: 'server', adapter: cloudflare(),})
Using themes from NPM
You can use any theme available on NPM with Expressive Code, provided that it exports a VS Code-compatible JSON theme object. Follow these steps:
-
Install the theme using your package manager of choice:
Terminal window # Install any theme from NPM here, e.g. `summer-time`npm i summer-timeTerminal window # Install any theme from NPM here, e.g. `summer-time`pnpm add summer-timeTerminal window # Install any theme from NPM here, e.g. `summer-time`yarn add summer-time -
Add the theme to your Expressive Code configuration:
astro.config.mjs import { defineConfig } from 'astro/config'import astroExpressiveCode from 'astro-expressive-code'// Import the theme objectimport summerTime from 'summer-time/themes/summer-time-vscode-theme.json'export default defineConfig({integrations: [astroExpressiveCode({// Pass the theme to the `themes` option// (consider adding a dark and light theme for accessibility)themes: [summerTime],}),],})astro.config.mjs import { defineConfig } from 'astro/config'import starlight from '@astrojs/starlight'// Import the theme objectimport summerTime from 'summer-time/themes/summer-time-vscode-theme.json'export default defineConfig({integrations: [starlight({title: 'My Starlight site',expressiveCode: {// Pass the theme to the `themes` option// (consider adding a dark and light theme for accessibility)themes: [summerTime],},}),],})next.config.mjs import createMDX from '@next/mdx'import rehypeExpressiveCode from 'rehype-expressive-code'// Import the theme objectimport summerTime from 'summer-time/themes/summer-time-vscode-theme.json'/** @type {import('rehype-expressive-code').RehypeExpressiveCodeOptions} */const rehypeExpressiveCodeOptions = {// Pass the theme to the `themes` option// (consider adding a dark and light theme for accessibility)themes: [summerTime],}/** @type {import('next').NextConfig} */const nextConfig = {reactStrictMode: true,pageExtensions: ["js", "jsx", "ts", "tsx", "md", "mdx"],}const withMDX = createMDX({extension: /\.mdx?$/,options: {remarkPlugins: [],rehypePlugins: [// The nested array structure is required to pass options// to a rehype plugin[rehypeExpressiveCode, rehypeExpressiveCodeOptions],],},})export default withMDX(nextConfig)
Using themes from VS Code
You can export any theme from VS Code for use with Expressive Code. Follow these steps:
-
Open the desired theme in VS Code using any of the following methods:
-
Browse the VS Code Themes gallery, select a theme, and press one of the
Open With
buttons to open it in VS Code. -
Alternatively, start right inside VS Code by pressing Ctrl+Shift+P, running the command
Preferences: Color Theme
and selecting a theme you like.
-
-
Export the theme to a file:
-
Inside VS Code, press Ctrl+Shift+P and run the command
Generate Color Theme from Current Settings
. -
Save the generated file to a folder of your website, e.g. as
./my-theme.jsonc
.
-
-
Add the theme to your Expressive Code configuration:
astro.config.mjs import { defineConfig } from 'astro/config'import astroExpressiveCode, { ExpressiveCodeTheme } from 'astro-expressive-code'import fs from 'node:fs'// Load your saved theme JSONC file here and create a theme from itconst jsoncString = fs.readFileSync(new URL(`./my-theme.jsonc`, import.meta.url), 'utf-8')const myTheme = ExpressiveCodeTheme.fromJSONString(jsoncString)export default defineConfig({integrations: [astroExpressiveCode({// Pass the theme to the `themes` option// (consider adding a dark and light theme for accessibility)themes: [myTheme],}),],})astro.config.mjs import { defineConfig } from 'astro/config'import starlight from '@astrojs/starlight'import { ExpressiveCodeTheme } from '@astrojs/starlight/expressive-code'import fs from 'node:fs'// Load your saved theme JSONC file here and create a theme from itconst jsoncString = fs.readFileSync(new URL(`./my-theme.jsonc`, import.meta.url), 'utf-8')const myTheme = ExpressiveCodeTheme.fromJSONString(jsoncString)export default defineConfig({integrations: [starlight({title: 'My Starlight site',expressiveCode: {// Pass the theme to the `themes` option// (consider adding a dark and light theme for accessibility)themes: [myTheme],},}),],})next.config.mjs import createMDX from '@next/mdx'import rehypeExpressiveCode, { ExpressiveCodeTheme } from 'rehype-expressive-code'import fs from 'node:fs'// Load your saved theme JSONC file here and create a theme from itconst jsoncString = fs.readFileSync(new URL(`./my-theme.jsonc`, import.meta.url), 'utf-8')const myTheme = ExpressiveCodeTheme.fromJSONString(jsoncString)/** @type {import('rehype-expressive-code').RehypeExpressiveCodeOptions} */const rehypeExpressiveCodeOptions = {// Pass the theme to the `themes` option// (consider adding a dark and light theme for accessibility)themes: [myTheme],}/** @type {import('next').NextConfig} */const nextConfig = {reactStrictMode: true,pageExtensions: ["js", "jsx", "ts", "tsx", "md", "mdx"],}const withMDX = createMDX({extension: /\.mdx?$/,options: {remarkPlugins: [],rehypePlugins: [// The nested array structure is required to pass options// to a rehype plugin[rehypeExpressiveCode, rehypeExpressiveCodeOptions],],},})export default withMDX(nextConfig)