Skip to content

fix(next-mdx): pin .mdx server pages to the rsc bundleLayer - metadata exports build under webpack#95057

Draft
icyJoseph wants to merge 1 commit into
canaryfrom
fix/mdx-metadata-rsc-layer
Draft

fix(next-mdx): pin .mdx server pages to the rsc bundleLayer - metadata exports build under webpack#95057
icyJoseph wants to merge 1 commit into
canaryfrom
fix/mdx-metadata-rsc-layer

Conversation

@icyJoseph

Copy link
Copy Markdown
Member

An App Router .mdx page that does export const metadata = {…} fails to build with webpack:

You are attempting to export "metadata" from a component marked with "use client"

@next/mdx registers its .mdx rule with the layer-agnostic options.defaultLoaders.babel, so the SWC pass runs with bundleLayer: undefined. That makes isReactServerLayer false, and as of 16.2 the RSC metadata guard rejects any non-react-server module that exports metadata, even without an actual "use client" directive. A .tsx page in the same dir is unaffected because Next's core layered rules only match /\.(tsx|ts|js|cjs|mjs|jsx)$/, so .mdx never gets the rsc loader and falls through to the layer-less one.

One possible fix is to pin the MDX SWC pass to the rsc bundle layer (App Router .mdx pages are Server Components), during the server compile step.

Also added app/metadata/page.mdx + an assertion in test/e2e/app-dir/mdx/mdx.test.ts (runs for both mdx-rs and non-rs) verifying the exported metadata renders.

Fixes #91735

@icyJoseph icyJoseph changed the title fix(next-mdx): pin .mdx server pages to the rsc bundle layer so metadata exports build under webpack fix(next-mdx): pin .mdx server pages to the rsc bundleLayer - metadata exports build under webpack Jun 22, 2026
@github-actions

github-actions Bot commented Jun 22, 2026

Copy link
Copy Markdown
Contributor

Stats cancelled

Commit: 6575da2
View workflow run

@github-actions

github-actions Bot commented Jun 22, 2026

Copy link
Copy Markdown
Contributor

Failing test suites

Commit: 6575da2 | About building and testing Next.js

pnpm test-dev test/development/app-dir/hmr-intercept-routes/hmr-intercept-routes.test.ts (job)

  • hmr-intercept-routes > should update intercept routes via HMR (DD)
Expand output

● hmr-intercept-routes › should update intercept routes via HMR

page.waitForSelector: Timeout 10000ms exceeded.
Call log:
  - waiting for locator('#default-intercept') to be visible

  569 |
  570 |     return this.startChain(async () => {
> 571 |       const el = await page.waitForSelector(selector, {
      |                             ^
  572 |         timeout,
  573 |         state,
  574 |       })

  at waitForSelector (lib/browsers/playwright.ts:571:29)
  at Playwright._chain (lib/browsers/playwright.ts:701:23)
  at Playwright._chain [as startChain] (lib/browsers/playwright.ts:682:17)
  at Playwright.startChain [as waitForElementByCss] (lib/browsers/playwright.ts:570:17)
  at Object.waitForElementByCss (development/app-dir/hmr-intercept-routes/hmr-intercept-routes.test.ts:38:21)

'@mdx-js/react',
require.resolve('./mdx-components.js'),
]
// App Router `.mdx` pages are Server Components, so pin the SWC pass to

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The @next/mdx plugin forces bundleLayer: 'rsc' on ALL server-side .mdx modules (gated only by options.isServer), so Pages Router .mdx pages and App Router .mdx files imported by client components are wrongly run through the React Server Components server-layer SWC transform.

Fix on Vercel

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

MDX export const metadata fails to build with webpack in 16.2.0 (regression from 16.1.6)

1 participant