Skip to content

IconBase chained destructure with rest patterns breaks when bundled by rolldown (Vite 8+) #155

@danfernand

Description

@danfernand

Bug

@phosphor-icons/react@2.1.8 fails at runtime with TypeError:
Cannot destructure 'null' as it is null when an app is bundled
by rolldown (the bundler now used by Vite 8.x). The error
originates from IconBase.es.js and prevents any icon from
rendering — the page is blank.

Confirmed in a 4-file minimal repro (no MUI, no emotion, no
app-level config): vite 8.0.16, @vitejs/plugin-react 5.2.0 (also
reproduces with 6.0.2), @phosphor-icons/react 2.1.8, react
18.3.1.

Root cause

dist/lib/IconBase.es.js chains two destructuring assignments —
both with rest patterns — into a single const:

  // dist/lib/IconBase.es.js, lines 5–20
  const {
    alt: n,
    color: r,
    size: t,
    weight: o,
    mirrored: c,
    children: i,
    weights: m,
    ...x
  } = s, {
    color: d = "currentColor",
    size: l,
    weight: f = "regular",
    mirrored: g = !1,
    ...w
  } = e.useContext(h);

When rolldown lowers this pattern, it correctly transpiles each
rest spread into a helper call (_(obj, knownKeys)) — but in the
process emits a junk {} = null statement after the second
destructure. Excerpt from the minified output:

  let l = C.useContext(ae),
      { color:u = `currentColor`, ..., mirrored:p = !1 } = l,
      m = _(l, oe),
      {} = null;   // ← rolldown injected this; throws at runtime

Reproduces on rolldown 1.0.1 and 1.0.3. So pinning rolldown
doesn't help.

Minimal reproduction

  // package.json
  {
    "type": "module",
    "dependencies": {
      "@phosphor-icons/react": "2.1.8",
      "react": "18.3.1",
      "react-dom": "18.3.1"
    },
    "devDependencies": {
      "@vitejs/plugin-react": "5.2.0",
      "vite": "8.0.16"
    }
  }

  // vite.config.mts
  import { defineConfig } from "vite";
  import react from "@vitejs/plugin-react";
  export default defineConfig({ plugins: [react()] });

  // src/main.tsx
  import { createRoot } from "react-dom/client";
  import { HeartIcon } from "@phosphor-icons/react";
  createRoot(document.getElementById("root")!).render(<HeartIcon
  size={32} />);

  <!-- index.html -->
  <!DOCTYPE html>
  <div id="root"></div>
  <script type="module" src="/src/main.tsx"></script>

Run: npm install --include=optional && npx vite build && npx
vite preview → open the preview URL → blank page + the
destructure-null error in the console. Inspect the built JS
bundle and search for {}=null to see the offending statement.

Suggested fix in IconBase

Splitting the chained const into two separate declarations
avoids the rolldown bug entirely — the rest-spread pattern is
fine on its own, only the chained form trips the compiler:

  // Before:
  const {
    alt: n, color: r, size: t, weight: o, mirrored: c, children:
  i, weights: m,
    ...x
  } = s, {
    color: d = "currentColor",
    size: l,
    weight: f = "regular",
    mirrored: g = !1,
    ...w
  } = e.useContext(h);

  // After:
  const {
    alt: n, color: r, size: t, weight: o, mirrored: c, children:
  i, weights: m,
    ...x
  } = s;
  const {
    color: d = "currentColor",
    size: l,
    weight: f = "regular",
    mirrored: g = !1,
    ...w
  } = e.useContext(h);

I'm running this exact change as a local yarn patch and it
produces a bundle without the {} = null statement; icons render
correctly.

Where the bug really lives

This is fundamentally a rolldown compiler bug — the original
chained destructure is valid JavaScript. I'll be filing it
upstream too. But because the Vite ecosystem is migrating to
rolldown right now (and Vite 8 is GA), any user upgrading their
Vite-based app to 8.x with @phosphor-icons/react will hit this.
Adjusting IconBase is a one-line code change for a real-world
unblock, regardless of the upstream timeline.

Environment

  • @phosphor-icons/react: 2.1.8
  • vite: 8.0.16
  • rolldown: 1.0.1 and 1.0.3 (both reproduce)
  • @vitejs/plugin-react: 5.2.0 and 6.0.2 (both reproduce)
  • Node: 20.19.0
  • macOS, Apple Silicon (no platform-specific element to the bug)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions