Apprentice

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:

terminal
pnpm add astro-expressive-code

Configuration

The integration goes into astro.config.ts. The key detail is ordering: Expressive Code before MDX.

astro.config.ts
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.

src/styles/global.css
: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.