Skip to content

Satteri MDX syntax highlighting bypasses components.pre for fenced code blocks #17340

Description

@koichiio

Astro Info

Astro                    v7.0.7
Vite                     v8.1.3
Node                     v22.22.3
System                   Linux (x64)
Package Manager          npm
Output                   static
Adapter                  none
Integrations             @astrojs/sitemap (v3.7.3)
                         @astrojs/mdx (v7.0.2)

If this issue only occurs in one browser, which browser is a problem?

No response

Describe the Bug

When using Satteri as Astro's Markdown processor with @astrojs/mdx, fenced
code blocks that are syntax highlighted do not render through a custom MDX pre
component passed via the Content components prop.

For example, this pattern works for normal MDX elements:

<Content
	components={{
		pre: Preformatted,
	}}
/>

But highlighted fenced code blocks bypass Preformatted. In my site, that means
code block filename rendering, language label rendering, wrapper markup, copy
button markup, and validation inside Preformatted.astro do not run.

I ran into the behavior while mostly hand-writing a refactor of my own
site from the previous unified-based Markdown pipeline to the Satteri-based
pipeline
.

Frontend and the JS/TS ecosystem are not my main area of focus, so I used an
LLM to help investigate the issue and identify the likely upstream files. The
LLM suggested the following possible cause (summary), and I looked into the
files. They seem related, but I'm not sure.

The behavior appears to come from the Satteri MDX path installing the highlight
plugin with `{ mdx: true }`:

https://github.com/withastro/astro/blob/main/packages/integrations/mdx/src/satteri/index.ts#L97-L101

That then makes the shared Satteri highlight plugin replace the original `pre`
HAST node with an MDX `<Fragment set:html={...} />` node:

https://github.com/withastro/astro/blob/main/packages/markdown/satteri/src/satteri-processor.ts#L112-L116
https://github.com/withastro/astro/blob/main/packages/markdown/satteri/src/satteri-processor.ts#L201-L218

As a result, MDX component mapping never sees a real `pre` element for
highlighted code fences.

What's the expected result?

I would expect one of these outcomes:

  1. Highlighted fenced code blocks in MDX still render through components.pre,
    so projects can wrap or validate code blocks the same way they can override
    other MDX HTML elements.
  2. If highlighted code blocks intentionally bypass components.pre, this
    behavior is documented clearly.

Ideally, custom pre components should still be able to receive the highlighted
pre output or its relevant props/children.

Link to Minimal Reproducible Example

https://github.com/koichiio/astro-satteri-mdx-pre-repro

Participation

  • I am willing to submit a pull request for this issue.

Metadata

Metadata

Assignees

Labels

- P4: importantViolate documented behavior or significantly impacts performance (priority)pkg: mdxIssues pertaining to `@astrojs/mdx` integrationtriage: needs triageIssue needs to be triaged

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions