withastro / astro · Issue No. 17555
Astro v7.1.6
Vite v8.1.5
Node v24.18.1
System Windows (x64)
Package Manager npm
Output static
Adapter none
Integrations @astrojs/mdx (v7.0.5)
Happens in FireFox and Chrome, didn't test others
When rendering an MDX content collection entry using render() from astro:content, enabling markdown.smartypants causes typographic punctuation to be corrupted into mojibake on Windows.
For example:
Don’t becomes Don’“Hello” becomes “Helloâ€This only occurs when rendering an entry from a content collection using render(). A direct .mdx page renders correctly.
I spent quite some time isolating the issue.
The .mdx file is saved as UTF-8.
I verified this by:
Format-Hex.The UTF-8 bytes are correct:
') → 27’) → E2 80 99The source file itself is not corrupted.
getCollection() returns the correct content.
For example:
console.log(lesson.body);
prints:
I don’t like it
which proves the content is read correctly before rendering.
A page such as:
src/pages/direct.mdx
renders perfectly:
Don’t
“I don’t like it”
No corruption occurs.
Rendering the same content using:
const { Content } = await render(lesson);
produces HTML containing:
Don’t
“I don’t like itâ€
Viewing the generated HTML source confirms the mojibake is already present before the browser renders the page.
Changing:
markdown: {
smartypants: false,
}
immediately fixes the issue.
The same content then renders correctly:
Don't
"I don't like it"
The issue appears to occur only when all of the following are true:
render() from astro:contentmarkdown.smartypants is enabledDirect .mdx pages do not exhibit this behavior, and the content returned by getCollection() is already correct before rendering.
This suggests the corruption occurs somewhere during the rendering pipeline rather than while reading the source file.
When rendering an MDX content collection entry using render() from astro:content, enabling markdown.smartypants should produce valid typographic punctuation without corrupting the output.
For example, the following Markdown:
Don't
Don’t
"I don't like it"
"I don’t like it"
should render as:
Don't
Don’t
“I don't like it”
“I don’t like it”
or, if Smartypants is expected to convert straight quotes:
Don’t
Don’t
“I don’t like it”
“I don’t like it”
In either case, the rendered HTML should contain valid Unicode punctuation (’, “, ”) and should never contain mojibake such as ’, “, or â€.
I will create that later - right now not in a position to do
Relay reads this issue against the repository's contribution signals: the files it is likely to touch, how the maintainers triage work this size, and what the first contribution would exercise.
The full analysis for this issue is still being assembled. Until then, the description above and the thread on GitHub are the most reliable context.