Astro Code Toggle Component
I built an Astro component called CodeToggle.astro
for my experimental site.
The idea was to create a simple wrapper around a React (or other interactive component) in an MDX file so that the source of that rendered component could be nicely displayed as a highlighted code block on the click of a toggle.
Usage looks like this:
import { default as TailwindCalendarV1 } from './components/TailwindCalendar.v1';import TailwindCalendarV1Source from './components/TailwindCalendar.v1?raw';
<CodeToggle source={TailwindCalendarV1Source}> <TailwindCalendarV1 client:load /></CodeToggle>;
The implementation of CodeToggle.astro
looked like this
---import { Code } from "astro/components";import { Code as CodeIcon } from "lucide-react";
interface Props { source: string; lang?: string; children: astroHTML.JSX.Element;}
const { source, lang = "tsx" } = Astro.props;---
<div class="relative"> <div class="mb-4"> <slot /> </div>
<div class="not-prose"> <details class="group"> <summary class="flex items-center gap-2 font-mono text-xs px-2 py-1 rounded-md bg-[var(--color-bg-code)] text-[var(--color-ink-light)] opacity-80 hover:opacity-100 hover:text-[var(--color-ink)] cursor-pointer transition-all duration-200 w-fit" > <CodeIcon className="w-3 h-3" /> <span class="select-none group-open:hidden">Show Source</span> <span class="select-none hidden group-open:block">Hide Source</span> </summary> <div class="mt-3 rounded-md overflow-hidden"> <Code code={source} lang={lang as any} theme="monokai" /> </div> </details> </div></div>
This approach was relatively straightforward and I thought I was using the <Code>
component in a sensible way.
I actually published my first post for the site using this component and thought things were going well.
Right after I attempted to publish a new post, I started running into strange build issues
02:37:59 ├─ /notes/2025/llm-tailwind-react/index.htmlhighlighter.codeToHtml is not a function Hint: This issue often occurs when your MDX component encounters runtime errors. Stack trace: at file:///vercel/path0/dist/chunks/index_C97_OQzq.mjs:69:34Error: Command "npm run build" exited with 1
I reverted a few commits locally, but the problem persisted.
From some debugging, it seemed the issue stemmed from my attempts to use shiki
, a syntax highlighting package, in multiple ways.
Why this all of a sudden became a problem, I was still unsure.
After some more poking around, searching GitHub and experimenting with different LLM outputs, I came up with these changes:
diff --git a/src/components/prose/CodeToggle.astro b/src/components/prose/CodeToggle.astroindex 88a758d..166c324 100644--- a/src/components/prose/CodeToggle.astro+++ b/src/components/prose/CodeToggle.astro@@ -1,6 +1,7 @@ ----import { Code } from "astro/components"; import { Code as CodeIcon } from "lucide-react";+import { createHighlighter } from "shiki";+import type { BundledLanguage } from "shiki";
interface Props { source: string;@@ -9,6 +10,16 @@ interface Props { }
const { source, lang = "tsx" } = Astro.props;++const highlighter = await createHighlighter({+ themes: ["monokai"],+ langs: [lang],+});++const html = highlighter.codeToHtml(source, {+ lang: lang as BundledLanguage,+ theme: "monokai",+}); ---
<div class="relative">@@ -28,9 +39,7 @@ const { source, lang = "tsx" } = Astro.props; <span class="select-none group-open:hidden">Show Source</span> <span class="select-none hidden group-open:block">Hide Source</span> </summary>- <div class="mt-3 rounded-md overflow-hidden">- <Code code={source} lang={lang as any} theme="monokai" />- </div>+ <div class="mt-3 rounded-md overflow-hidden" set:html={html} /> </details> </div> </div>
With that approach, the build issues resolved. I can’t say I quite understand why I started having this issue but hopefully this post helps anyone who runs into the same.
Recommended
Intro to Astro
I'm aiming to setup a space for more interactive UX experiments. My current Hugo blog has held up well with my scale of content but doesn't play...