The css must not disappear when the js was loaded.
The css disappears. This does not happen in webpack development, but only in production.
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
];
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!)
| Tech | Version |
|--------------|---------|
| Material-UI | v3.0.3 |
| React | 16.5.1 |
| Browser | Chrome x64 Latest, Windows |
Reproduction repo: https://github.com/davalapar/template
yarn run devyarn run prodyarn run finalyarn run nodemonWhen you build the client with production settings, you need to run the server also with NODE_ENV='production' as it changes the generated class names.
Thanks for responding, will look into it.
Hi @evik42, I tried running it with that var set but I'm still getting the same disappearing css.
NODE_ENV=production node dist/server/main.js
$ NODE_ENV=production node dist/server/main.js
Warning: connect.session() MemoryStore is not
designed for a production environment, as it will leak
memory, and will not scale past a single process.
Listening @ localhost:80
Listening @ localhost:443
Same NODE_ENV setting approach at https://github.com/mui-org/material-ui/commit/956e59e7fec2466596e7f3238fc7332ea99bfa74
Update:
dangerouslyUseGlobalCSS being true right?@evik42's analysis is correct. You need to make the server render in production mode. NODE_ENV=production yarn run nodemon to the trick.
@oliviertassinari thanks for responding. I tried it but unfortunately the css still disappears once the js files are loaded.
Screenshot here: https://i.imgur.com/BIjA7mk.png
Tried it with changes in webpack.config.js too :
I also tested the generateClassName to ensure they're working on server and client side and they're indeed producing matching name of classes.
Same result of css disappearing when js loads, while the component's onClick work without problems.. Could there be anything else I've overlooked here?
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).
Adding NODE_ENV=production to my server script has fixed the issue for me. Script command is now NODE_ENV=production run-s build start-prod
Most helpful comment
Adding
NODE_ENV=productionto my server script has fixed the issue for me. Script command is nowNODE_ENV=production run-s build start-prod