Hey there!
When writing technical articles, having a good code highlighter to present code snippets is mandatory. I tried a few of them but only recently became fully convinced by a new code highlighter: Expressive Code.
Expressive Code is based on Shiki and extends its capabilities. Unlike other code highlighters, like Prism, Shiki is meant to run at build-time or during server-side rendering to ship zero JavaScript to the browser. Shiki and Expressive Code accept any VS Code theme as a valid theme configuration.
Expressive Code has native Astro support. It is enabled by default on Starlight, the template for documentation websites made by Astro's team. Expressive Code also provides a remark plugin for use in any other environment.
Shiki supports many programming languages, so you should be good to go even with the most esoteric ones.
The first characteristic of Expressive Code that differentiates it from many other solutions is that it can highlight lines of code by default.
When defining your code blocks in Markdown, you can specify the lines you want to highlight next to the language of the snippet:
It renders the following code block:
It's also possible to mark some lines as insertions or deletions with the ins
and del
attributes:
It renders the following code snippet:
Expressive Code also supports diffs without losing the syntax highlighting of the code block's programming language. I've often tried using a diff
code block to highlight changes with other syntax highlighters, but the syntax highlighting was always lost.
Renders that beautiful diff while preserving JavaScript highlighting:
It's also possible to give labels to the diffs:
It will render the labels in the margin:
Expressive Code determines the frame to use based on the language of the code block. For shell scripts, the frame looks like a macOS terminal; for source code, it looks like a tab in a code editor.
The frame of a shell script:
The frame can be titled either by providing the title attribute to the code block or by the first line of the code block being a comment:
Renders the following frames:
And a shell frame with a title looks like this:
Expressive Code has two optional plugins. The first plugin is to create collapsible sections:
The second plugin is to add line numbers:
It's important to mention that every code block includes a button to copy its content, which appears when the block is hovered over. I like having this as a default behavior.
Expressive Code supports using multiple themes. By default, it has a theme for light mode and another for dark mode.
On XState by Example, it renders like that:
Using multiple themes doesn't dramatically increase the HTML bundle size because it relies on CSS variables to style the code blocks' tokens. Expressive Code doesn't duplicate all the HTML content for each theme—that's smart!
<Code />
componentThere is one last significant benefit of using Expressive Code, and it's a recent addition: the <Code />
component of the Astro's integration.
For XState by Example, I want to display the source code of each demo below the demo. Thanks to the <Code />
component, I can get syntax highlighting for dynamic code snippets.
I get the raw content of the source files and display their content with Expressive Code:
---
import { Code } from "astro-expressive-code/components";
const { default: machineCode } = await import(
`../examples/${Astro.params.machine}/machine.ts?raw`
);
---
<Code code={machineCode} lang="ts" title="machine.ts" />
Astro doesn't support rendering such dynamic content, so I had to find a horrible escape hatch to make it work before the <Code />
component became a thing.
I'm delighted to use Expressive Code to highlight the code blocks on my website, XState by Example. I will now use it every time I need to display code snippets on a website.
Expressive Code is perfect and deserves much more love, so let's spread it worldwide!
Best,
Baptiste