Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Module build failed: Error: The style-loader cannot be used in a non-browser environment #109

Closed
wzup opened this issue Feb 9, 2016 · 24 comments

Comments

@wzup
Copy link

wzup commented Feb 9, 2016

What error is this? How to resolve?

@hellectronic
Copy link

It would be helpful to post your webpack config.

@Xazzzi
Copy link

Xazzzi commented Feb 18, 2016

@wzup you are using style loader, but you have no DOM where you can insert your <style> or <link> tags in your enviroment. Check this line from source:

if(typeof document !== "object") throw new Error("The style-loader cannot be used in a non-browser environment");

You're probably executing your bundle code with nodejs, and i'm not sure why do you want css for server-side code.

@ChrisRus
Copy link

Building a site compiler that embeds webpack. The build process generates many of the files consumed by webpack and my intention is to include bootstrap CSS and glyphicons. I've actually got both server-side and client-side bundles being produced, and the final build step loads the webpacked entry scripts for each generated page in the site and pre-renders at build time (on Node.js).

But that fails when my webpacked server-side render code hits the style-loader code injected by webpack into the bundle.

/home/cdr/code/encapsule/snapsite/docs/output/server/02413fb0.js:36219
                        return /msie [6-9]\b/.test(window.navigator.userAgent.toLowerCase());
                                                   ^

In a little more detail, the failing 'node' build of this page's entry bundle is leveraging ReactDOMServer to affect the server-side rendering. And, of course this loader knows/cares nothing about that.

Wondering if anyone has figured out how to get inlined CSS working server side?

Thanks.

@jspears
Copy link

jspears commented Mar 2, 2016

@ChrisRus - I am getting something really similar, at however I am not compiling for a server. -

/msie [6-9]\b/.test(({}).navigator.userAgent.toLowerCase());

Can not imagine under what circumstances this would work. Somebody would have to have modified
Object.prototype.navigator. I do not remember any browser that does it, and would be really really bad.

in a file that looks like

 function(module, exports, __webpack_require__) {

    /*
        MIT License http://www.opensource.org/licenses/mit-license.php
        Author Tobias Koppers @sokra
    */

@magalhas
Copy link

magalhas commented Mar 2, 2016

I've actually created a PR that I think it solves this, at least if you're using local scopes. Check out #115

@jspears
Copy link

jspears commented Mar 2, 2016

@magalhas thanks, been thinking that should be there for a while, i've run into this a few times.

-To the universe-
It would be really swell if style-loader could expose the needed css to the server somehow. Need some ideas here, so that the page could be put together on the server and include the css in the original render server side.

This would be extremely server dependent, perhaps an option that outputs a json file with an array of css files to include. So a server could do what the wanted by requiring said file. I know a bit magical, but would allow for styles to be included in sync with the client....

@wzup
Copy link
Author

wzup commented Mar 3, 2016

@Xazzzi,

but you have no DOM where you can insert your <style> or <link> tags in your enviroment

Sounds reasonable. I do have React server rendering.

I don't want css for my server side code. I use React components from React-toolbox project (http://react-toolbox.com/#/components) and those guys embed styles into components. Don't know how to solve it yet in an elegant way without much hacking.

@Xazzzi
Copy link

Xazzzi commented Mar 3, 2016

@wzup, you can add your style-including code into bundle conditionally - omit it for server-side bundle.

One way of doing that is using webpack.DefinePlugin in conjunction with webpack.optimize.UglifyJsPlugin, like react does for error messages.

Then you can write conditions like:

if (process.env.BUNDLE_FOR === 'client') { 
  require('style!whatever.css')
}

If block will not appear in bundle unless in your webpack.confing you will have:

new webpack.DefinePlugin({'process.env': {BUNDLE_FOR: '"client"'}})

Because of dead code elimination UglifyJsPlugin does.

ADD: I mised the part about react-toolbox, the above wouldn't be helpful for you, probably.
Maybe replacing loader config for server-side bundle with some placeholder one would help.

I guess something like this may work:

module.exports = function() {}
module.exports.pitch = function(remainingRequest) {
  return ''
}

@BaoDelta
Copy link

I'm using webpack to compile my server code with React rendering and have this problem as well. The PR of @magalhas looks good. Can someone please test and merge it?

In the mean time, my workaround is using https://github.com/kriasoft/isomorphic-style-loader in webpack server config.

@cchamberlain
Copy link

@wzup @BaoDelta - What works best for me is shoving the style loading bit of my libraries into the componentDidMount hook of the react component. This lifecycle hook will not get called until client rendering occurs. See https://github.com/cchamberlain/react-hatch/blob/master/src/lib/index.js#L77 for a working example.

@ChrisRus
Copy link

Suggestions made by @cchamberlain and @Xazzzi seem reasonable to me and require nothing more than clear separation of concerns so that code paths that are not required server-side do not get executed in that context.

@cchamberlain suggests an approach that would work in my specific scenario that leverages React's lifecycle callbacks to ensure that the style loader doesn't get required in unless the component(s) that require the stylesheet actually mount (on the DOM). It's likely not significant but worth mentioning that the loader and the stylesheets would still however be included in the server-side bundle.

@Xazzzi suggestion is more generally applicable as he simply suggests excluding the superfluous modules from the bundle using webpack.DefinePlugin (clever thanks for that).

Certainly this approach would work for my scenario where all the React components I'm working with are custom (i.e. I control the source so can punch in conditional logic around require of the style loader). But this isn't really practical for react-toolbox as was mentioned.

Maybe the right approach is to hijack the loader regex \.css$/ and bind it to a no-op in the server-side webpack.config.js? This would, I think, effectively cut the style loader and stylesheet out of the bundle and shim all existing calls to style loader to code that's safe to run under Node.js?

@cchamberlain
Copy link

@ChrisRus that also sounds like a good approach to me although I would think it would be more apt to attract the react dev errors telling you your server and client markup differ. I'd be interested to see if that is more or less performant with the styles not delivered with the initial bundle. TBH I think the best approach would be if the style-loader didn't attempt to run window / document oriented code when they don't exist. This seems like it should be a simple change to me but I have done no research there. I've recently moved over to postcss loader with css modules and extract text plugin and have not noticed the issue occurring even with the style loading outside of componentDidMount.

@catmando
Copy link

catmando commented Jul 6, 2016

I just ran into this problem, and I observe that the react-bootstrap (for example) code does this:

/* 82 */
/***/ function(module, exports) {

    'use strict';
    is_document_defined = !!(typeof window !== 'undefined' && window.document && window.document.createElement);
    module.exports = !!(typeof window !== 'undefined' && window.document && window.document.createElement);

/***/ },

then

    var canUseDOM = __webpack_require__(82);

to prevent issues.

Could something similar be done here?

@marianhlavac
Copy link

Kinda disappointed that style-loader is client-only. How come no one ever noticed this when writing isomorphic React app?

@catmando
Copy link

catmando commented Jul 7, 2016

So I took the multiple entry point approach, and that worked well:

I just made an entry point called client_only, and loaded up anything related strictly to client there, and then that gets included only on the client side. Problem solved.

@sibelius
Copy link

@catmando could u please post a snippet of how to do this multiple entry point approach to solve this problem?

@AlastairTaft
Copy link

@mmajko You have to use something like jss, react-look or isomporhic-stlye-loader. Would be nice if the style-loader checked if it was in a browser environment before adding styles to the document. So that it wouldn't cause runtime errors server side.

@amannn
Copy link

amannn commented Aug 21, 2016

I'm also running into this problem in development mode with an app that renders on both the server and the client.

In production mode it's no problem, since I'm using the extract text plugin for styles, but in development mode style loader breaks my app. My expected behaviour is that styles are just ignored on the server side. The only workaround I can think of is compiling the app separately for the server with style loader removed, but I'd like to avoid that because I'd have to compile twice on every file change.

I think PR #115 would have solved my problem. If you don't want it, Is there an easy fix for this?

@cchamberlain
Copy link

@amannn Yes, the easiest way to avoid in a React app is to move your style loading into requires within componentDidMount component lifecycle hook since they are not executed server side.

I am working on a universal-style-loader drop-in replacement for style loader but the docs are not exactly pristine and I'd label it experimental at this stage. I am using it successfully in a few projects at this time so feel free to use and let me know if you experience issues.

@michael-ciniawsky
Copy link
Member

style-loaders propose is to add <style></style> to the DOM, use extract-text-plugin or #114 for other proposed solutions, implementing server-side rendering without a DOM has low/to none priority atm.

@danhdoancs
Copy link

Stumbled upon this issue too and I found an every simple solution. You just need to use ExtractTextPlugin on the server side for css-loader to extract CSSs without going through style-loader. Then, we could just ignore that duplicated css file without any harm.

This is the config for css loader:
{ test: /\.css$/, exclude: /node_modules/, loader: ExtractTextPlugin.extract({ fallback: "style-loader", use: "css-loader?modules&importLoaders=1&localIdentName=[name]__[local]--[hash:base64:5]" }) }

@creeperyang
Copy link

I forked style-loader and make the iso-morphic-style-loader. Not sure whether it will fit your needs, but it support style-loader work with node env, and used with isomorphic app quite well.

iso-morphic-style-loader

@bolonio
Copy link

bolonio commented Jul 26, 2018

@creeperyang Thanks, that solves it :)

@jcmcneal
Copy link

@creeperyang your changes solve this issue. You should open a PR with your changes and hopefully they'll get merged in!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests