I'm using Fluxible to help create an isomorphic app on a new project and it's going swimmingly. I love it so far. I have run into a speed bump, though, and wonder how to get over it.
Here is my Header component thus far:
import React from 'react'
import Nav from '../Nav/Nav'
import classNames from 'classnames'
if (process.env.BROWSER) var styles = require('./Header.css')
class Header extends React.Component {
render() {
// Header classes
var theClasses = process.env.BROWSER ? classNames({
[styles.Header]: true
}) : ''
return (
<header className={theClasses}>
<Nav selected={this.props.selected} links={this.props.links} />
</header>
)
}
}
export default Header
You'll see I'm using process.env.BROWSER
to detect which ENV I'm on. If we're in the client, I require the CSS. If we're on the server, I skip it. That works wonderfully.
The problem comes later in the file, where I build theClasses
object based on the contents of the Header.css
file, then use those classes on the Header like so:
<header className={theClasses}>
<Nav selected={this.props.selected} links={this.props.links} />
</header>
The problem is because I'm not loading the css on the server, theClasses
ends up being empty, and the content rendered for the client ends up different than the content on the server. React displays this warning:
Warning: React attempted to reuse markup in a container but the checksum was invalid. This generally means that you are using server rendering and the markup generated on the server was not what the client was expecting. React injected new markup to compensate which works but you have lost many of the benefits of server rendering. Instead, figure out why the markup being generated is different on the client or server:
(client) n28"><header class="Header--Header_ip_OK
(server) n28"><header class="" data-reactid=".2ei
What would you recommend to get this resolved?
The original issue was that I couldn't get CSS to compile on the server side, so I started checking for the BROWSER like this:
if (process.env.BROWSER) var styles = require('./Application.css')
If I remove the if (process.env.BROWSER)
bit I get this error:
SyntaxError: src/components/Application/Application.css: Unexpected token (2:0)
1 |
> 2 | @import 'styles/index.css';
| ^
3 |
In the following simple CSS file:
@import 'styles/index.css';
.Application {
box-shadow: 0 0 0 1px var(--medium-gray);
box-sizing: border-box;
lost-center: 1080px 32px;
}
I started this project with the Fluxible Yo Generator which provides two Webpack config files here: https://github.com/yahoo/generator-fluxible/tree/master/app/templates
I updated mine with a few loaders:
var webpack = require('webpack');
var path = require('path');
module.exports = {
resolve: {
extensions: ['', '.js', '.jsx']
},
entry: [
'webpack-dev-server/client?http://localhost:3000',
'webpack/hot/only-dev-server',
'./client.js'
],
output: {
path: path.resolve('./build/js'),
publicPath: '/public/js/',
filename: 'main.js'
},
module: {
loaders: [
{
test: /\.(js|jsx)$/,
exclude: /node_modules/,
loaders: [
require.resolve('react-hot-loader'),
require.resolve('babel-loader')
]
}, {
test: /\.css$/,
loader: 'style-loader!css-loader?modules&localIdentName=[name]__[local]_[hash:base64:5]!postcss-loader'
}, {
test: /\.(png|jpg|svg)$/,
loader: 'url?limit=25000'
}, {
test: /\.json$/,
loader: 'json-loader'
}
]
},
postcss: function () {
return [
require('lost'),
require('postcss-import')({
path: ['./src/'],
onImport: function (files) {
files.forEach(this.addDependency);
}.bind(this)
}),
require('postcss-mixins'),
require('postcss-custom-properties'),
require('autoprefixer')({
browsers: ['last 3 versions']
})
];
},
node: {
setImmediate: false
},
plugins: [
new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/),
new webpack.HotModuleReplacementPlugin(),
new webpack.NoErrorsPlugin(),
new webpack.DefinePlugin({
'process.env': {
NODE_ENV: JSON.stringify(process.env.NODE_ENV),
BROWSER: JSON.stringify(true)
}
})
],
devtool: 'eval'
};
So that's where I am… not sure how to get CSS compiled server side. Appreciate any help I can get.
You should certainly be rendering your server side HTML with the classes otherwise you are missing out on the time to glass perks of an isomorphic application. It means you will still have to wait for the JS to load in order to apply the CSS styles which defeats some of the purpose of building the HTML server side. It also means you can't "cut the mustard" and serve old browsers an application with Javascript turned off.
The question is why aren't you rendering the CSS classes on the server? This also confused me for a while but my guess is you don't have two Webpack entry points? One for the client, and one for the server.
If my assumption is correct then take a look at a sandbox repo here where I'm doing multiple builds for the Node server entrypoint and the Browser entrypoint also using Fluxible.
However I would take it all with a pinch of salt for now as it was just a test project for personal use. I have also used this approach with local css where it builds it on both the server and client and it works like a charm.
EDIT: I see you are using ES6 so I assume you are indeed building server side? If so what is the reason you don't include the CSS?