Summary
Every docs page that uses the <CodeBlock> MDX component (most of content/docs/*.mdx, e.g. quickstart.mdx) fails to render, showing the generic "This page didn't load" error boundary instead of content. Confirmed on /docs/quickstart, reproducible in both vite dev and a production vite build + vite preview, in a completely fresh browser tab (rules out HMR/module-cache staleness).
Root symptom
Browser console:
Error: CodeBlock is not defined
at http://localhost:8080/docs/quickstart:...
This is a raw ReferenceError, not fumadocs' own friendly _missingMdxReference message ("Expected component CodeBlock to be defined..."), which means the compiled MDX module's const {Callout, CodeBlock} = _components destructuring line is not actually running before the <CodeBlock> JSX usage sites evaluate -- or _components genuinely lacks CodeBlock at runtime despite the source wiring looking correct.
What I ruled out
- Not a Vite dep-cache issue: cleared
node_modules/.vite and the generated .source/ fumadocs output, restarted the dev server fresh -- still reproduces.
- Not browser-tab/HMR state: reproduces in a brand-new tab on first navigation.
- Not dev-only: reproduces identically on a
vite build + vite preview production bundle.
- Not a missing import:
docsMdxComponents (src/lib/docs-mdx-components.tsx) correctly imports and maps CodeBlock from src/components/site/primitives.tsx, which is a plain top-level export function CodeBlock(...) with no circular-import risk (only imports lucide-react, react, sonner, @/lib/utils).
- The compiled MDX output looks structurally correct when fetched directly (
curl 'http://localhost:8080/content/docs/quickstart.mdx?collection=docs'): _createMdxContent(props) does const _components = {...defaults, ...props.components}, {Callout, CodeBlock} = _components; and later references the destructured CodeBlock identifier consistently across all 4 usage sites in the file -- this is standard, valid MDX-compiler output shape.
- The prop-passing chain looks correct on paper:
docs.quickstart.tsx's route component calls docsClientLoader.getComponent(path) then renders <Content /> (no props) -> fumadocs-mdx's Renderer(props) -> our custom component(doc, props) in src/lib/docs-client-loader.tsx, which ignores the passed-in props and unconditionally returns <MDXContent components={docsMdxComponents} /> -- so MDXContent's own props.components should always be docsMdxComponents, regardless of what <Content /> was called with.
Where I'd look next
Given static tracing of the whole chain (docs.quickstart.tsx -> docsClientLoader -> fumadocs-mdx/dist/runtime/browser.js's Renderer/use() -> compiled MDXContent/_createMdxContent) all looks correct, but the runtime behavior disagrees, I'd suspect either:
- A genuine
fumadocs-mdx/MDX-compiler version mismatch in how the components prop merges into the compiled module's scope (worth trying a fumadocs-mdx version bump or diffing its browser runtime against its own test fixtures).
- Something about the
use() + Suspense timing in Renderer causing _createMdxContent to execute in a stale/detached closure on a subsequent render pass.
- Instrumenting
component({default: MDXContent}, props) in docs-client-loader.tsx with a temporary console.log(props, docsMdxComponents) and reproducing live would settle this quickly -- I did not want to leave debug instrumentation in place without a clear fix.
Scope
Affects /docs/quickstart confirmed; likely affects most/all of the ~50 docs.*.tsx routes, since CodeBlock (and probably Callout, which has the identical destructuring pattern) is used broadly across content/docs/*.mdx.
Why filed separately
Found incidentally while manually verifying the UI for #7567 (unrelated fairness-analytics work) -- filing separately since it's a pre-existing, unrelated bug that deserves focused investigation rather than being bolted onto that PR.
Summary
Every docs page that uses the
<CodeBlock>MDX component (most ofcontent/docs/*.mdx, e.g.quickstart.mdx) fails to render, showing the generic "This page didn't load" error boundary instead of content. Confirmed on/docs/quickstart, reproducible in bothvite devand a productionvite build+vite preview, in a completely fresh browser tab (rules out HMR/module-cache staleness).Root symptom
Browser console:
This is a raw
ReferenceError, not fumadocs' own friendly_missingMdxReferencemessage ("Expected componentCodeBlockto be defined..."), which means the compiled MDX module'sconst {Callout, CodeBlock} = _componentsdestructuring line is not actually running before the<CodeBlock>JSX usage sites evaluate -- or_componentsgenuinely lacksCodeBlockat runtime despite the source wiring looking correct.What I ruled out
node_modules/.viteand the generated.source/fumadocs output, restarted the dev server fresh -- still reproduces.vite build+vite previewproduction bundle.docsMdxComponents(src/lib/docs-mdx-components.tsx) correctly imports and mapsCodeBlockfromsrc/components/site/primitives.tsx, which is a plain top-levelexport function CodeBlock(...)with no circular-import risk (only importslucide-react,react,sonner,@/lib/utils).curl 'http://localhost:8080/content/docs/quickstart.mdx?collection=docs'):_createMdxContent(props)doesconst _components = {...defaults, ...props.components}, {Callout, CodeBlock} = _components;and later references the destructuredCodeBlockidentifier consistently across all 4 usage sites in the file -- this is standard, valid MDX-compiler output shape.docs.quickstart.tsx's route component callsdocsClientLoader.getComponent(path)then renders<Content />(no props) ->fumadocs-mdx'sRenderer(props)-> our customcomponent(doc, props)insrc/lib/docs-client-loader.tsx, which ignores the passed-in props and unconditionally returns<MDXContent components={docsMdxComponents} />-- soMDXContent's ownprops.componentsshould always bedocsMdxComponents, regardless of what<Content />was called with.Where I'd look next
Given static tracing of the whole chain (
docs.quickstart.tsx->docsClientLoader->fumadocs-mdx/dist/runtime/browser.js'sRenderer/use()-> compiledMDXContent/_createMdxContent) all looks correct, but the runtime behavior disagrees, I'd suspect either:fumadocs-mdx/MDX-compiler version mismatch in how thecomponentsprop merges into the compiled module's scope (worth trying a fumadocs-mdx version bump or diffing its browser runtime against its own test fixtures).use()+ Suspense timing inRenderercausing_createMdxContentto execute in a stale/detached closure on a subsequent render pass.component({default: MDXContent}, props)indocs-client-loader.tsxwith a temporaryconsole.log(props, docsMdxComponents)and reproducing live would settle this quickly -- I did not want to leave debug instrumentation in place without a clear fix.Scope
Affects
/docs/quickstartconfirmed; likely affects most/all of the ~50docs.*.tsxroutes, sinceCodeBlock(and probablyCallout, which has the identical destructuring pattern) is used broadly acrosscontent/docs/*.mdx.Why filed separately
Found incidentally while manually verifying the UI for #7567 (unrelated fairness-analytics work) -- filing separately since it's a pre-existing, unrelated bug that deserves focused investigation rather than being bolted onto that PR.