Server Rendering / SSR : CSS disappears upon js load, only in prod not dev
See original GitHub issue- This is not a v0.x issue.
- I have searched the issues of this repository and believe that this is not a duplicate.
Expected Behavior
The css
must not disappear when the js
was loaded.
Current Behavior
The css
disappears. This does not happen in webpack development, but only in production.
Steps to Reproduce
I was following the examples from the docs.
I noticed that the one from our docs here at https://material-ui.com/guides/server-rendering/ is different from the one from this github repo which is https://github.com/mui-org/material-ui/blob/master/docs/src/pages/guides/server-rendering/server-rendering.md, in which case I ended up following the latter since it solved some initial problems such as css name mismatches between the server & client. I also ensured that I’ve read the Troubleshooting part and didn’t miss what’s documented already.
The webpack.config.js
I’m using is provided below, and I also tried commenting out the following parts related to splitChunks and runtimeChunk, but the css
still disappears when the bundle js
is loaded in the production build:
// ...
optimization: {
/*
* SplitChunks: {
* chunks: 'all'
* },
* runtimeChunk: true,
*/
minimize: Boolean(mode === 'production'),
minimizer: [
new UglifyJSWebpackPlugin({
parallel: os.cpus().length,
cache: true,
uglifyOptions: {
output: {
comments: false
},
compress: {
dead_code: true
},
mangle: true
},
sourceMap: true
})
]
},
// ...
App.jsx:
import React from 'react';
import Button from '@material-ui/core/Button';
const App = () => (
<Button variant="contained" color="primary" onClick={() => console.log('Clicked!')}>
Hello World
</Button>
);
export default App;
Client.jsx:
import '@babel/polyfill';
import React from 'react';
import { hydrate } from 'react-dom';
import {
MuiThemeProvider,
createMuiTheme,
createGenerateClassName
} from '@material-ui/core/styles';
import green from '@material-ui/core/colors/green';
import red from '@material-ui/core/colors/red';
import JssProvider from 'react-jss/lib/JssProvider';
import App from './App';
class Main extends React.Component {
componentDidMount () {
const jssStyles = document.getElementById('jss-server-side');
if (jssStyles && jssStyles.parentNode) {
jssStyles.parentNode.removeChild(jssStyles);
}
}
render () {
return <App />;
}
}
const theme = createMuiTheme({
palette: {
primary: green,
accent: red,
type: 'light'
}
});
const generateClassName = createGenerateClassName({
dangerouslyUseGlobalCSS: false
});
hydrate(
(
<JssProvider generateClassName={generateClassName}>
<MuiThemeProvider theme={theme}>
<Main />
</MuiThemeProvider>
</JssProvider>
),
document.querySelector('#root'),
);
SSR.jsx:
import React from 'react';
import { renderToString } from 'react-dom/server';
import { SheetsRegistry } from 'react-jss/lib/jss';
import JssProvider from 'react-jss/lib/JssProvider';
import {
MuiThemeProvider,
createMuiTheme,
createGenerateClassName
} from '@material-ui/core/styles';
import green from '@material-ui/core/colors/green';
import red from '@material-ui/core/colors/red';
import App from '../client/App';
const handleRender = (req, res) => {
const sheetsRegistry = new SheetsRegistry();
const sheetsManager = new global.Map();
const theme = createMuiTheme({
palette: {
primary: green,
accent: red,
type: 'light'
}
});
const generateClassName = createGenerateClassName({
dangerouslyUseGlobalCSS: false
});
const html = renderToString(
<JssProvider registry={sheetsRegistry} generateClassName={generateClassName}>
<MuiThemeProvider theme={theme} sheetsManager={sheetsManager}>
<App />
</MuiThemeProvider>
</JssProvider>
);
const css = sheetsRegistry.toString();
const page = `
<!doctype html>
<html>
<head>
<title>Material-UI</title>
</head>
<body>
<div id="root">${html}</div>
<style id="jss-server-side">${css}</style>
<script src="/scripts/main.js" defer></script>
<script src="/scripts/vendors~main.js" defer></script>
<script src="/scripts/runtime~main.js" defer></script>
</body>
</html>
`;
res.send(page);
};
export default handleRender;
//...
import SSR from './SSR';
const app = express();
// ...
app.use(SSR);
webpack.config.js:
const os = require('os');
const path = require('path');
const webpack = require('webpack');
const UglifyJSWebpackPlugin = require('uglifyjs-webpack-plugin');
const webpackNodeExternals = require('webpack-node-externals');
const Client = (env, argv) => {
const { mode } = argv;
return {
devtool: mode === 'development'
? 'source-map'
: false,
entry: [
'@babel/polyfill',
'./src/client/Client.jsx'
],
resolve: {
extensions: [
'.js',
'.jsx'
]
},
module: {
rules: [
{
enforce: 'pre',
test: /\.(js|jsx)$/,
use: 'eslint-loader',
exclude: /node_modules/
},
{
test: /\.worker\.js$/,
use: [
{
loader: 'worker-loader',
options: {
name: '[name].js',
publicPath: '/scripts/'
}
}
]
},
{
test: /\.(js|jsx)$/,
use: 'babel-loader',
exclude: /node_modules/
},
{
test: /\.(css)$/,
use: [
'style-loader',
'css-loader'
]
}
]
},
plugins: [
new webpack.DefinePlugin({
ENVIRONMENT: JSON.stringify(mode)
})
],
optimization: {
splitChunks: {
chunks: 'all'
},
runtimeChunk: true,
minimize: Boolean(mode === 'production'),
minimizer: [
new UglifyJSWebpackPlugin({
parallel: os.cpus().length,
cache: true,
uglifyOptions: {
output: {
comments: false
},
compress: {
dead_code: true
},
mangle: true
},
sourceMap: true
})
]
},
output: {
path: path.join(__dirname, '/dist/client'),
publicPath: '/scripts/'
},
stats: 'minimal'
};
};
const Server = (env, argv) => {
const { mode } = argv;
return {
devtool: mode === 'development'
? 'source-map'
: false,
entry: ['./src/server/Server.js'],
resolve: {
extensions: [
'.js',
'.jsx'
]
},
target: 'node',
node: {
__dirname: false,
__filename: false
},
externals: [webpackNodeExternals()],
module: {
rules: [
{
enforce: 'pre',
test: /\.(js|jsx)$/,
use: 'eslint-loader',
exclude: /node_modules/
},
{
test: /\.(js|jsx)$/,
use: 'babel-loader',
exclude: /node_modules/
}
]
},
plugins: [
new webpack.DefinePlugin({
ENVIRONMENT: JSON.stringify(mode),
})
],
output: {
path: path.join(__dirname, '/dist/server')
}
};
};
module.exports = [
Client,
Server
];
Context
Nothing much special just App.jsx
with a button.
When the bundle js
is loaded, the components work great. Their styling just disappears, that’s the problem.
(preparing the reproduction repo right now!)
Your Environment
Tech | Version |
---|---|
Material-UI | v3.0.3 |
React | 16.5.1 |
Browser | Chrome x64 Latest, Windows |
Issue Analytics
- State:
- Created 5 years ago
- Comments:8 (2 by maintainers)
Top GitHub Comments
Adding
NODE_ENV=production
to my server script has fixed the issue for me. Script command is nowNODE_ENV=production run-s build start-prod
It was working all fine on my side with your reproduction. webpack has nothing to do with it. It’s about using the same generator on the client and the sever (same options, same node env, same version).