Astro (v4) - Configure Expressive Code with Markdoc
Posted on Apr 28, 2024 in Blog Posts
At time of writing, package versions are:
-
astro
:4.7.0
-
@astro/markdoc
:0.11.0
-
astro-expressive-code
:0.35.2
Overview
This post explores a basic Astro setup utilizing Markdoc and Expressive Code blocks.
The setup will:
- Render code fences parsed with Markdoc as Expressive Code blocks with proper language highlighting
- Work with Expressive Code's frame auto-parsing commented file names as code block titles
This setup will not:
- Parse titles from the fences because of a limitation with the Markdoc parser, but it will provide an alternative using Markdoc attributes.
Prerequisites
- A base Astro project
- Routing set up to serve Markdoc files to test that Expressive Code blocks are rendering
Configuration
Configure Expressive Code
- If
@astrojs/markdoc
isn't already installed, add it to your project with - In your
astro.config.mjs
file, configure Expressive Code themes and other desired options. I set a theme and the tab width.
Configure Markdoc
- If
@astrojs/markdoc
isn't already installed, add it to your project with - In your
astro.config.mjs
file, configure desired Markdoc options. I use the default options here. - If you don't already have a
markdoc.config.mjs
in the project's root directory, create it now and copy/paste this base configuration for later.
Fence component
Create the component
Value | Markdoc attribute | Expressive Code prop |
---|---|---|
Code content | content | code |
Language | language | lang |
Because the Expressive Code prop names and Markdoc fence node attribute names don't map one-to-one, astro-expressive-code
's Code
component can't be used to replace the default Markdoc fence
component in the config.
Create a component that maps between Markdoc's fence node attributes and Expressive Code's component props to alleviate this issue.
- In
src/components
, create aFence.astro
file. - Write a component that imports the
Code
component from Expressive Code.- Map Markdoc's
content
to Expressive Code'scode
prop. - Map Markdoc's
language
to Expressive Code'slang
prop. - Pass the
title
andframe
attributes to their respective props
- Map Markdoc's
Use the new component in Markdoc
In the Markdoc config file, override the @astrojs/markdoc
renderer with our new component. In order to use Expressive Code's rendering logic, don't spread the other fence node properties from @astrojs/markdoc
's fence node.
If the @astrojs/markdoc
fence node transformer is included, Expressive Code will error when rendering the code blocks.
- The default
@astrojs/markdoc
fence transformer adds content to the defaultslot
. - Expressive Code's
Code
component has a check that nothing is rendered in the defaultslot
, and this is not currently a configurable option.
Test the new component
Load a page with a code block and it should be displayed in an Expressive Code code block.
Try using different markdown scenarios and see what happens. Here are a few to get you started.
Remove backslashes (\
) from the upcoming Markdoc examples. The backslashes were added to avoid parsing errors.
Example: {\% title="My Title" %}
should not have the \
between the {%
.
Render a code block with a file name as a code comment
The markdown:
The result:
Render a code block with a title attribute
The markdown:
The result:
Render a frame as a terminal frame instead of a code frame
The markdown:
The result: