Add inline syntax highlighting to Astro

doge head admiring inline highlighting and saying wow such highlighting

I started using Astro to build my site at the end of 2023. It’s been a great experience overall, but one thing has bothered me: there’s no inline syntax highlighting in markdown files. It’s especially frustrating because the syntax highlighting in code blocks is great. But check this out: Math.max(1, 2). I figured out how to get it working!

Astro uses Shiki under the hood to handle syntax highlighting. It can highlight a ton of languages out of the box, it comes with plenty of great themes, and it’s fast. But ultimately, it comes bundled with Astro, so that’s what I use.

Prior to August 2022, inline highlighting wasn’t a feature of Shiki. But now that it’s been added (v1.15.0), and now that Astro 5 has updated Shiki to v1.23.1 (in the @astrojs/markdown-remark package), we can use it in Astro.

Here’s how I added it to my site:

  • Add the Shiki rehype plugin to your project:

    npm add -D @shikijs/rehype
    
  • Update your astro.config.ts file to include this plugin:

    import rehypeShiki from "@shikijs/rehype"
    ...
    export default defineConfig({
      ...
      markdown: {
        rehypePlugins: [
          [
            rehypeShiki,
            {
              inline: "tailing-curly-colon",
              theme: "one-dark-pro",
            },
          ],
        ],
        shikiConfig: {
          theme: "one-dark-pro",
        },
      },
    });
    
  • Use the correct… syntax, to start using this feature:

    This markdown:

    Log your log with `console.log(log)`
    

    Changes into this:

    Log your log with `console.log(log){:js}`
    

Do you see the new curly brace syntax at the end of the second example? You include the language specifier that you want in there and Shiki does the rest.

Alternatives

If you don’t want to use the Shiki plugin, there are a couple of alternative options for adding inline syntax highlighting.

Use the Astro <Code /> component

Astro provides two components to render code blocks in .astro and .mdx files: <Code /> and <Prism />, but only the <Code /> component can provide inline highlighting:

<Code code={`awesomeFunction()`} lang="js" inline />

I prefer to use the Shiki solution above. Using the <Code /> component feels like using a pile driver to hammer in a nail. Plus, you can’t use it in regular markdown files. But it exists and might be a better solution for some.

Use CSS

Yes, good old CSS can work in a pinch. This is how I got highlighting to work when I started using Astro. My solution only provided highlighting in one style:

code {
  padding: 5px 2px;
  background-color: #646464;
  border-radius: 2px;
}

That looks like this: awesomeFunction()

Not great, but it’s something 🤷.

Adding a custom language

What if Shiki doesn’t include support for a language out of the box? You can add your own! In another life, I used AutoHotKey on Windows and wrote about it on this site. Shiki doesn’t support highlighting AutoHotKey scripts natively, so you need to add a custom language. Per the documentation:

You can load custom languages by passing a TextMate grammar object into the langs array.

There’s an AutoHotKey VS Code extension that provides syntax highlighting and it includes the grammar object that can be used by Shiki (in the LSP). Here’s how you can add it:

  • Download the grammar object here and save it to your project. I placed it in src/grammars:.

  • Update the shikiConfig section of astro.config.ts to reference the new grammar object:

    import ahkGrammar from "./src/grammars/ahk2.tmLanguage.json";
    ...
    export default defineConfig({
      ...
      markdown: {
        ...
        shikiConfig: {
          langs: [{
            ...ahkGrammar,
            aliases: ["ahk"], // 👈️ A shorter specifier
          }]
          ...
        },
      },
    });
    
  • Start using the custom highlighting:

    This markdown block:

    ```ahk
    ; Shortcut for work email
    ::emi::
      SendInput your.name@example.com
      TrayTip, , Text 'emi' Expanded, 2
    Return
    ```
    

    Will end up looking like this:

    ; Shortcut for work email
    ::emi::
      SendInput your.name@example.com
      TrayTip, , Text 'emi' Expanded, 2
    Return
    

Final thoughts

I’m happy that my inline code can now be highlighted and it didn’t require a whole lot of work. But it is a bit annoying to have to add the specifier at the end of each instance. I’m pretty sure that you’ll be seeing me missing the colon in the future (myBadCode{js}).

Overall, it was a fun exercise to get everything wired up and make my site look a small bit better. Good luck implementing this solution on your own site! If you come up with an improvement, please let me know using one of those social links below.