Run client-side JS in Astro MDX
By Flavio Copes
Learn how to run client-side JavaScript inside an Astro MDX file, where a plain script tag breaks, by wrapping your code in a template literal.
To run client-side JavaScript in an Astro MDX file, wrap the code inside the script tag in a JSX expression with a template literal. A plain script tag, the one you’d use in a .astro file, gets mangled by the MDX compiler.
Here’s how I found out.
I wanted to add some bits of client-side JS in a .mdx file in Astro.
Just a one-liner to redirect after 2 seconds, nothing too crazy:
setTimeout(() => (location.href = '/'), 2000)
In a .astro component I’d normally use:
<script>
setTimeout(() => (location.href = '/'), 2000)
</script>
but this didn’t work because in the browser what we get is (due to MDX compiler, escaping, etc):
<script><p>setTimeout(() => (location.href = ’/’), 2000)</p></script>
Why does the plain script tag break?
MDX is markdown first. The content between the script tags is treated as markdown text, not as code.
So the compiler wraps the line in a <p> tag, escapes > into =>, and turns the straight quotes into curly ones. The browser receives HTML inside a script tag, which is not valid JavaScript, and nothing runs.
The fix
I ended up using this syntax:
<script>{`
setTimeout(() => (location.href = '/'), 2000)
`}</script>
and this worked fine.
The curly braces switch MDX into JSX expression mode. Inside them, markdown parsing is off. The template literal is a plain JavaScript string, and MDX passes it to the script tag exactly as written. No escaping, no <p> wrapper.
Watch out for backticks in your code
There’s one catch with this trick. Your code lives inside a template literal, so two characters need escaping.
If the code itself contains a backtick, or a ${ sequence, the template literal breaks. Escape them with a backslash, \`` and ${`, and you’re fine.
For anything longer than a few lines this gets annoying. In that case my advice is to move the code into a small .astro component with a regular script tag, and import that component into the MDX file. You keep normal script handling, and the MDX file stays clean.
Related posts about astro: