I am working on an isomorphic React+Flux+express app and using webpack loaders for my sass (with sass-loader) and jsx files. I am not sure how to inject my stylesheets into a server side template. I took a look at the Extract Text Plugin for this purpose, but I really want to be able to use hot module replacement.
Right now, I am loading my main.scss
file in a React ponent like this:
if (typeof window !== 'undefined') {
require("!style!css!sass!../../../styles/main.scss");
}
This works well for loading an individual stylesheet in a ponent, but there is a flicker before the React part is mounted. I understand that this is because this is injecting the stylesheet after my client-side app has loaded, so the stylesheet is not immediately available. This leads to the actual question: is there a way to inject this style-sheet into my server-side template while still using the webpack loader, or does this call for a separate gulpfile or express middleware? I was previously using a gulpfile to build my stylesheets, but I will end up having a lot of stylesheets and don't want them all jammed into one file.
I am working on an isomorphic React+Flux+express app and using webpack loaders for my sass (with sass-loader) and jsx files. I am not sure how to inject my stylesheets into a server side template. I took a look at the Extract Text Plugin for this purpose, but I really want to be able to use hot module replacement.
Right now, I am loading my main.scss
file in a React ponent like this:
if (typeof window !== 'undefined') {
require("!style!css!sass!../../../styles/main.scss");
}
This works well for loading an individual stylesheet in a ponent, but there is a flicker before the React part is mounted. I understand that this is because this is injecting the stylesheet after my client-side app has loaded, so the stylesheet is not immediately available. This leads to the actual question: is there a way to inject this style-sheet into my server-side template while still using the webpack loader, or does this call for a separate gulpfile or express middleware? I was previously using a gulpfile to build my stylesheets, but I will end up having a lot of stylesheets and don't want them all jammed into one file.
Share Improve this question asked Feb 17, 2015 at 19:36 johnnyutahjohnnyutah 6851 gold badge7 silver badges23 bronze badges3 Answers
Reset to default 13So the idea here is to have webpack pile with two separate configurations, one targeted for web
(the browser), the other targeted for node
(server-side). The server-side bundle can be required in other node/express code to build the pre-rendered HTML with the css.
There's a full example here, and I'll walk you through the relevant parts of it. https://github./webpack/react-starter
The prerender.html
in app
is the server-side template the author is using. Notice the following two lines of code:
<link rel="stylesheet" href="STYLE_URL">
<script src="SCRIPT_URL"></script>
See the config for webpack here https://github./webpack/react-starter/blob/master/make-webpack-config.js. The options being passed into here depends on whether you're doing a prod build or a dev build. Since we want to build the client bundle and the prerendering server bundle, let's take a look at https://github./webpack/react-starter/blob/master/webpack-production.config.js. It's creating two bundles, specifically the first one with separate stylesheets targetted for browser, and the second config is for prerendering.
For the first pilation, it uses this:
plugins.push(new ExtractTextPlugin("[name].css" + (options.longTermCaching ? "?[contenthash]" : "")));
to create a separate css file alongside your bundle. During the second pilation (for prerendering), it uses null-loader
to load the styles (because we already have the styles we needed in a css file, we can just use that).
Now here's where we inject the path to css into your server template.
Take a look here at a simplified server.js
: https://github./webpack/react-starter/blob/8e6971d8fc9d18eeef7818bd6e9be45f6b8643e6/lib/server.js
var STYLE_URL = "main.css?" + stats.hash;
var SCRIPT_URL = [].concat(stats.assetsByChunkName.main)[0];
app.get("/*", function(req, res) {
res.contentType = "text/html; charset=utf8";
res.end(prerenderApplication(SCRIPT_URL, STYLE_URL, COMMONS_URL));
});
Assuming your output path for your bundle is the same as server.js (otherwise you can get the publicPath using require("../build/stats.json").publicPath
and prepend it to your STYLE_URL
and SCRIPT_URL
above.
Then in your prerender.jsx
: https://github./webpack/react-starter/blob/8e6971d8fc9d18eeef7818bd6e9be45f6b8643e6/config/prerender.jsx
Grab your server-side template prerender.html
and replace the URLs:
var html = require("../app/prerender.html");
module.exports = function(scriptUrl, styleUrl, monsUrl) {
var application = React.renderComponentToString(<Application />);
return html.replace("STYLE_URL", styleUrl).replace("SCRIPT_URL", scriptUrl).replace("COMMONS_URL", monsUrl).replace("CONTENT", application);
};
I admit this can be plicated and confusing, and if it's easier to use a separate gulpfile go for it. But do play around with this. If you need more clarification and help, you can post a ment and I'll get to it as soon as I can or you can use the webpack chatroom here (https://gitter.im/webpack/webpack), I'm sure one of the developers there can probably give you a better explanation than I did.
Hope this is somewhat(?) helpful!
The initial flash appears because "style-loader" hasn't yet downloaded your CSS styles.
To resolve this issue, isomorphic (universal) rendering is required (markup generation on server, and not just in production mode - in development mode too).
You can achieve isomorphic rendering either by following the "react-starter" project path (mentioned in the ment above), or by using webpack-isomorphic-tools
https://github./halt-hammerzeit/webpack-isomorphic-tools
You will need 2 webpack builds: 1 for web, 1 for node.
If you remove style, you will be able to load your stylesheet on both the server and client.
var css = require("!css!sass!../../../styles/main.scss");
Since you aren't using the style-loader anymore, you will need to manually inject the resulting css into your view.
In your view you will need to include this
<style>{css.toString()}</style>
That should work on both server and client. When you do React.renderToString(), the css will be parsed in whatever order it is in your html. If it is at the top/in the <head>
, there should be no flicker.