Expressive Code with Tokyo Night
Why Expressive Code
Astro ships with Shiki for syntax highlighting, but Expressive Code adds features that make code blocks genuinely useful for a technical blog: file name tabs, line highlighting, word wrapping, a copy button, and frame styles that look like a real editor.
In Astro 6, Expressive Code must come before MDX in the integrations array. Get this wrong and your code blocks will not pick up the theme.
Installation
Expressive Code is a single package:
pnpm add astro-expressive-codeConfiguration
The integration goes into astro.config.ts. The key detail is ordering: Expressive Code before MDX.
import { defineConfig, fontProviders } from "astro/config";import svelte from "@astrojs/svelte";import expressiveCode from "astro-expressive-code";import mdx from "@astrojs/mdx";
export default defineConfig({ integrations: [ svelte(), expressiveCode({ themes: ["tokyo-night"], styleOverrides: { borderRadius: "4px", codeFontFamily: "var(--font-mono)", }, defaultProps: { wrap: true, }, }), mdx(), ],});Using Code Blocks
Expressive Code activates automatically on fenced code blocks in markdown and MDX.
File Name Tabs
Add a title attribute to show a file name tab above the code block:
```ts title="src/data/site-data.ts"export const site = { name: "Wandering Monster",};```That renders with a tab showing the file path, exactly like a code editor.
Line Highlighting
Highlight specific lines with the {lines} syntax:
```ts {3,5-7}const config = { site: "https://wanderingmonster.dev", output: "static", integrations: [ svelte(), expressiveCode({ themes: ["tokyo-night"] }), mdx(), ],};```Word Wrap
Word wrap is enabled by default in our config. Long lines will soft-wrap rather than requiring horizontal scrolling. This matters on mobile and for accessibility.
Font Integration
The codeFontFamily override references our CSS variable var(--font-mono), which resolves to Fira Code via the Astro 6 Fonts API. When FiraCode Nerd Font .woff2 files are added to src/fonts/, the CSS variable will pick up the local version with ligature and icon support.
:not(pre) > code { font-family: var(--font-mono), "Fira Code", "Cascadia Code", monospace; font-size: 0.875em; background-color: var(--code-bg); padding: 0.15em 0.4em; border-radius: 4px;}The Result
Every code block on this site uses Tokyo Night colours, the Fira Code font family, file name tabs when specified, and line highlighting where it helps the reader. The copy button is built in. No additional JavaScript is shipped to the client beyond what Expressive Code generates at build time.
That is the entire setup. One integration, one theme, and a few style overrides. The next post will cover the Astro 6 Fonts API and how Bunny Fonts are self-hosted automatically.