How to theme Shiki syntax highlighting in Astro
Every code block on this blog is colored — keywords one shade, strings another, comments dimmed. I did not install a highlighter, write any CSS for it, or add a client-side script. Astro does it at build time with a tool called Shiki. Here is how I configured it and what is actually going on.
What Shiki is and why build-time matters
Shiki is a syntax highlighter that uses the same grammar and themes as VS Code, so your code blocks look exactly like your editor. Astro ships with it built in — you write a fenced code block in Markdown or MDX and Astro highlights it for you.
The key detail: this happens at build time, not in the browser. Shiki turns your code into already-colored HTML during astro build. That means:
- No highlighting JavaScript is shipped to visitors, so pages stay fast.
- The colors are baked into the HTML as inline styles, so there is no flash of unstyled code.
The real configuration
Highlighting is configured in astro.config.mjs under markdown.shikiConfig. Here is the relevant slice from this blog:
// blog/astro.config.mjs
export default defineConfig({
// ...
markdown: {
processor: unified({
remarkPlugins: [remarkReadingTime, remarkMermaid],
rehypePlugins: [rehypeCodeToolbar],
}),
shikiConfig: {
theme: "github-dark",
wrap: true,
},
},
// ...
})Two options, and that is genuinely all it takes to control the look of every code block on the site.
Choosing a theme
theme: "github-dark",theme picks the color scheme. I use github-dark because it matches the site’s dark aesthetic. Shiki ships with dozens of built-in themes, all borrowed from the VS Code ecosystem — common ones include:
github-darkandgithub-lightdraculanordmonokaione-dark-provitesse-darkandvitesse-light
To switch themes, change that one string and rebuild. Because the colors are inlined at build time, the whole site updates at once — there is no separate stylesheet to keep in sync.
If you want to support light and dark mode, Shiki also accepts a themes object with light and dark keys instead of a single theme, but for a dark-only site like this one, a single theme string is simpler.
Wrapping long lines
wrap: true,By default a long line of code overflows and forces a horizontal scrollbar on the block. Setting wrap: true tells Shiki to wrap long lines onto the next line instead. On a blog that is usually the friendlier choice: readers on narrow screens do not have to scroll sideways to read a single line. If you would rather keep long lines on one line with a scrollbar, set it to false (or leave it out — false is the default).
How it fits into the pipeline
Shiki is not the whole story of a code block on this site. Look again at the config:
processor: unified({
remarkPlugins: [remarkReadingTime, remarkMermaid],
rehypePlugins: [rehypeCodeToolbar],
}),
shikiConfig: { theme: "github-dark", wrap: true },Astro runs Markdown through a remark/rehype pipeline — remark works on the Markdown structure, rehype works on the resulting HTML. Shiki does the coloring, and then rehypeCodeToolbar (a custom rehype plugin in this repo) adds extra markup around the highlighted block — think a copy button or a filename toolbar.
The order to understand:
flowchart LR A["```ts code fence```"] --> B[Shiki colors the code] B --> C[rehypeCodeToolbar wraps it] C --> D[Final HTML in the page]
This matters if you ever style code blocks yourself: Shiki controls the colors inside the block via inline styles, while the surrounding toolbar/container markup comes from the custom plugin and your own CSS — often the same Tailwind v4 @theme tokens that style the rest of the blog. They live at different layers, so they do not fight — you theme the code through shikiConfig, and you style the wrapper through CSS.
Trying it
- Open
astro.config.mjs. - Under
markdown.shikiConfig, setthemeto any built-in Shiki theme name. - Optionally set
wrap: truefor reader-friendly wrapping. - Rebuild — every fenced code block updates automatically.
You do not touch individual posts at all; the config governs the whole site.
What to remember
- Astro highlights code with Shiki at build time — no client-side JS, no flash.
- Set the look with
markdown.shikiConfiginastro.config.mjs. theme: "github-dark"picks a VS Code theme; dozens of built-ins are available by name.wrap: truewraps long lines instead of forcing horizontal scroll.- Shiki colors the code itself; custom rehype plugins and CSS handle the wrapper around it, at a separate layer.