Skip to content

fix(css): rewrite MiniCssExtractPlugin insert function to ES5 to support legacy browsers#90556

Open
sleitor wants to merge 1 commit intovercel:canaryfrom
sleitor:fix/css-insert-es5-compat
Open

fix(css): rewrite MiniCssExtractPlugin insert function to ES5 to support legacy browsers#90556
sleitor wants to merge 1 commit intovercel:canaryfrom
sleitor:fix/css-insert-es5-compat

Conversation

@sleitor
Copy link

@sleitor sleitor commented Feb 26, 2026

What?

Rewrite the insert function passed to MiniCssExtractPlugin in packages/next/src/build/webpack/config/blocks/css/index.ts to use only ES5-compatible syntax.

Why?

The insert option of mini-css-extract-plugin serialises the function via Function#toString() and injects the resulting string directly into the webpack runtime chunk. This string bypasses all subsequent transpilation — it is not processed by Babel/SWC, and is not subject to the project's browserslist or output.environment webpack config.

The current function contains:

const { href, onload, onerror } = linkTag   // ES2015 destructuring
onload?.call(linkTag, ...)                  // ES2020 optional chaining

When the project targets legacy browsers (e.g. chrome >= 45) pages crash on load with:

Unexpected token {

before any CSS has been applied.

How?

Replace the destructuring with individual var declarations and replace optional chaining with explicit if null-checks:

Before:

const { href, onload, onerror } = linkTag
// ...
() => onload?.call(linkTag, { type: 'load' } as Event),
() => onerror?.call(linkTag, {} as Event)

After:

var href = linkTag.href
var onload = linkTag.onload
var onerror = linkTag.onerror
// ...
function () { if (onload) onload.call(linkTag, { type: 'load' } as Event) },
function () { if (onerror) onerror.call(linkTag, {} as Event) }

Runtime behavior is identical in all environments. The var style was intentionally chosen (over splitting const declarations) since no-var is disabled in the project ESLint config.

Fixes #90518

The mini-css-extract-plugin insert() option accepts a function which is
serialized via Function#toString() and injected directly into the browser
runtime bundle. This string bypass all subsequent transpilation steps.

Previously the function used:
  - const destructuring: const { href, onload, onerror } = linkTag
  - optional chaining:   onload?.call(...)

Destructuring assignment is not supported before Chrome 49 / Firefox 47.
When a project targets legacy browsers (browserslist: chrome >= 45),
these pages crash with 'Unexpected token {' before any CSS is loaded.

Fix: use var declarations with individual property access, and replace
optional chaining with explicit null checks. Both are ES5-compatible and
produce identical runtime behavior in all environments.

Fixes vercel#90518
@nextjs-bot
Copy link
Collaborator

Allow CI Workflow Run

  • approve CI run for commit: 3c6708a

Note: this should only be enabled once the PR is ready to go and can only be enabled by a maintainer

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

_N_E_STYLE_LOAD insert function in CSS loader is not transpiled, breaks on old browsers

2 participants