Skip to content

validate-imports false positive on sideEffects:false barrels (unused leaf kills build) #4007

Closed

Description

@AndreasGalster2

Versions: vinext 1.2.0, @vitejs/plugin-rsc 0.5.37, vite 8,
@heroui/react 3.2.1, react-aria-components 1.18.0, react 19.

Repro: https://github.com/AndreasGalster2/vinext-barrel-repro
(npm install && npm run build). A server component importing ONE
component from a barrel:

import { Toast } from "@heroui/react";

export default function Page() {
  return (
    <main>
      <h1>vinext barrel repro</h1>
      <Toast.Provider />
    </main>
  );
}

Build fails with:

[plugin rsc:validate-imports] .../react-aria-components/dist/exports/Calendar.mjs
Error: 'client-only' cannot be imported in server build ('rsc' environment):
 imported by .../react-aria-components/dist/exports/Calendar.mjs
  imported by .../@heroui/react/dist/components/calendar-year-picker/use-calendar-state.js
   imported by .../@heroui/react/dist/components/calendar-year-picker/index.js
    imported by .../@heroui/react/dist/index.js
     imported by app/page.tsx

Note the failing leaf (Calendar) is never used by the page — the barrel
drags it in. Same failure via other leaves (e.g. toast/index.js ->
toast-queue.js, which no subpath import can break).

Why false positive: both libraries ship sideEffects: false, so the
bundler drops every unused branch including the client-only marker —
Next.js builds this exact graph fine and serves it correctly in
production. The validator walks re-exports pre-shake, so any barrel ending
in a client-only-marked module fails even though the marker can never
evaluate at runtime.

Workarounds (verified): official subpaths for pure-barrel chains
(@heroui/react/skeleton); explicit "use client" boundary for chains
ending in real directive-less modules. Client bundles identical either way.

Suggested fix: teach the validator about sideEffects (skip subtrees
the bundler will drop), or validate post-tree-shake.

Activity

  1. james-elicx commented on Oct 15, 2026

    @james-elicx
    Member

    The build failure is because the RSC plugin does its checks before tree-shaking, which is where the import is removed. I've opened a PR there to propose moving the check to after tree-shaking - vitejs/vite-plugin-react#1511

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

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions