While attempting to upgrade react-select from version 2.4.4 to the latest 3.0.8 I'm running into issues with server side rendering and the following error:
ErrorDetails : ReferenceError: window is not defined
at ./node_modules/react-select/dist/base/dist/react-select-cac0a5ae.browser.esm.js (server.min.js:117809:17) -> var canUseDOM = !!(window.document && window.document.createElement);
We use this within a .Net CMS by way of ReactJS.Net so the window object is not available while it is being rendered on the server. The strange thing is that looking at the dist output from the npm package, the check for window being undefined isn't there:

However, looking at the src it is:

The check did exist in the dist output of the 2.4.4 version so something with the build process may have been changed with version 3.0.0 that could have caused this? Thank you for looking at the issue!
@swensorm Have you found any solution?
I have a workaround in place for the time being, but it's really not ideal as it renders nothing server side so there's a delay to it appearing in the browser. The exception would be thrown just from the static import, since the offending code isn't part of the component, so I have to dynamically require it.
if (typeof window === 'undefined') {
return null;
}
const { default: Select, components } = require('react-select'); // eslint-disable-line global-require
The bundle you use on the server should be built with webpacks target: 'node' to resolve modules from 'main' or 'module' fields and not using client-side specific aliases.
Since you're not running it under nodejs (ReactJS.NET does not provide full nodejs environment) you will get "require is not defined" when trying to use bundle built like this but there is away.
In the server build, keep target: 'web' (default when unspecified) but change resolve.aliasFields to an empty array, like this:
module.export = {
...
resolve: {
aliasFields: []
}
}
This way webpack will not use browser specific aliases when resolving react-select module... Ufff... :)
I was able to solve a similar issue with "window is not defined" when using SSR with reactjs.net by using your fix @mgrzyb .
Issue was described in ticket https://github.com/marcelltoth/react-select-reborn/issues/8.
While I can confirm that adding aliasFields: [] to the webpack config did indeed fix the drop-down rendering server side, it had the unfortunate side effect of causing over 1 MB of locale data from the Intl package to be included as well.
While I can confirm that adding
aliasFields: []to the webpack config did indeed fix the drop-down rendering server side, it had the unfortunate side effect of causing over 1 MB of locale data from theIntlpackage to be included as well.
Same :)
Why can't you check if windows is defined on canUseDOM() function?
I'd like to use some of hacks mentioned above but:
1) I can't conditionally render this component because my SSR config is still going through all the code and encounters this canUseDOM method even in case when it's not rendered.
2) I can't modify my webpack config due to project configuration (it's create-react-app based template and ejecting would cause much damage).
In the code, it looks like there is a check for typeof window !== 'undefined'
However, in the compiled version I see:
// node_modules/react-select/dist/Select-9fdb8cd0.browser.esm.js:430s
var canUseDOM = !!(window.document && window.document.createElement);
Manually updating that code fixes the issue. Why does the code in the git repo differ from the built version on NPM?
For those looking for a potential fix, here are there solutions:
{
resolve: {
alias: {
"react-select": require.resolve(
"react-select/dist/react-select.cjs.prod.js"
),
},
},
}
I think we are going to go with option 1...
I found the culprit. Looks like preconstruct js is replacing things quietly:
https://github.com/preconstruct/preconstruct/blob/master/packages/cli/src/build/rollup.ts#L137
Most helpful comment
The bundle you use on the server should be built with webpacks target: 'node' to resolve modules from 'main' or 'module' fields and not using client-side specific aliases.
Since you're not running it under nodejs (ReactJS.NET does not provide full nodejs environment) you will get "require is not defined" when trying to use bundle built like this but there is away.
In the server build, keep target: 'web' (default when unspecified) but change resolve.aliasFields to an empty array, like this:
This way webpack will not use browser specific aliases when resolving react-select module... Ufff... :)