Class name hydration mismatch in server and client, (Warning: Prop `className` did not match Server and Client)
See original GitHub issueI don’t know my issue is a bug or just a support term or just a configuration mismach, but I spend much time, Not happy go lucky writing here to somebody settle my issue. no!
I searched for 3 days, Even just sleep about 10 hours in this 3 days, try many ways, even I use Gitter but no one answer me, I see issues #10982 and #11506 and many Stack Overflow
question and answers but I couldn’t fix my issue and didn’t find right way.
Absolutely I read and read many times the Material-UI
Docs but I exactly did what the Docs said.
At last, I see class name hydration mismatch in server and client, 😞 this warning:
Warning: Prop `className` did not match. Server: "MuiFormLabel-root-134 MuiInputLabel-root-129 MuiInputLabel-formControl-130 MuiInputLabel-animated-133" Client: "MuiFormLabel-root-10 MuiInputLabel-root-5 MuiInputLabel-formControl-6 MuiInputLabel-animated-9"
I swear I tried many ways and searched a lot. I’m using Material-UI
version 1.2.1
.
This is my Index
component as a root component:
import React, {Component} from 'react';
import Helmet from 'react-helmet';
import styles from 'StylesRoot/styles.pcss';
import TextField from '@material-ui/core/TextField';
export default class App extends Component {
render() {
return (
<div className={styles['root-wrapper']}>
<Helmet
htmlAttributes={{lang: 'fa', amp: undefined}}
bodyAttributes={{dir: 'rtl'}}
titleTemplate='اسکن - %s'
titleAttributes={{itemprop: 'name', lang: 'fa'}}
meta={[
{name: 'description', content: 'صفحه اتصال اعضاء'},
{name: 'viewport', content: 'width=device-width, initial-scale=1'},
]}
link={[{rel: 'stylesheet', href: '/dist/styles.css'}]}
/>
<TextField label='test' helperText='help'/>
</div>
);
};
}
Here below you can see my server.jsx
and client.jsx
:
//--------------------------------------------client.jsx
import React from 'react';
import {hydrate} from 'react-dom';
import {BrowserRouter} from 'react-router-dom';
import {MuiThemeProvider, createMuiTheme} from '@material-ui/core/styles';
import {lightBlue, red} from '@material-ui/core/colors';
import Index from './app/index';
import RTL from './app/public/rtl';
const theme = createMuiTheme({
palette: {
primary: lightBlue,
accent: red,
type: 'light',
},
direction: 'rtl',
});
class Main extends React.Component {
// Remove the server-side injected CSS.
componentDidMount() {
const jssStyles = document.getElementById('jss-server-side');
if (jssStyles && jssStyles.parentNode) {
jssStyles.parentNode.removeChild(jssStyles);
}
}
render() {
return (
<BrowserRouter>
<Index {...this.props}/>
</BrowserRouter>
);
}
}
hydrate((
<RTL>
<MuiThemeProvider theme={theme}>
<Main/>
</MuiThemeProvider>
</RTL>
), document.getElementById('root'));
//--------------------------------------------server.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 {StaticRouter} from 'react-router-dom';
import {Helmet} from "react-helmet";
import {MuiThemeProvider, createMuiTheme, createGenerateClassName} from '@material-ui/core/styles';
import {red, lightBlue} from '@material-ui/core/colors';
import Template from './app/template';
import Index from './app/index';
import RTL from './app/public/rtl';
export default function serverRenderer({clientStats, serverStats}) {
return (req, res, next) => {
const context = {};
const sheetsRegistry = new SheetsRegistry();
const theme = createMuiTheme({
palette: {
primary: lightBlue,
accent: red,
type: 'light',
},
direction: 'rtl',
});
const generateClassName = createGenerateClassName();
const markup = renderToString(
<JssProvider registry={sheetsRegistry} generateClassName={generateClassName}>
<RTL>
<MuiThemeProvider theme={theme} sheetsManager={new Map()}>
<StaticRouter location={req.url} context={context}>
<Index/>
</StaticRouter>
</MuiThemeProvider>
</RTL>
</JssProvider>
);
const helmet = Helmet.renderStatic();
const jss = sheetsRegistry.toString();
res.status(200).send(Template({
markup: markup,
helmet: helmet,
jss: jss,
}));
};
}
So now it’s needed to template.jsx
:
export default ({ markup, helmet, jss }) => {
return `<!DOCTYPE html>
<html ${helmet.htmlAttributes.toString()}>
<head>
${helmet.title.toString()}
${helmet.meta.toString()}
${helmet.link.toString()}
<style id='jss-server-side'>${jss}</style>
</head>
<body ${helmet.bodyAttributes.toString()}>
<div id='root'>${markup}</div>
<script src='/dist/client.js' async></script>
</body>
</html>`;
};
Ok, Now it is possible that this question is asked What is RTL
? and Why you wrap MuiThemeProvider
inside it in both server and client?
So first see RTL
component:
import React from 'react';
import {create} from 'jss';
import rtl from 'jss-rtl';
import JssProvider from 'react-jss/lib/JssProvider';
import {createGenerateClassName, jssPreset} from '@material-ui/core/styles';
const jss = create({plugins: [...jssPreset().plugins, rtl()]});
const generateClassName = createGenerateClassName();
export default props => (
<JssProvider jss={jss} generateClassName={generateClassName}>
{props.children}
</JssProvider>
);
I saw it in Material-UI
Docs and I believe the documentation is a little poor and could be improved. I asked myself, Why documentation pass a props.children
for this function? and I guess maybe this function should wrap something. so I try many shapes and it works. but in the first call in browser after build. when I refresh the browser then the warning appears 😞 and I see this damn shape:
Surely I wanna see below shape, but I see it just once after build:
I don’t know what is going wrong. I put this question on Stack Overflow
too. And upload a mini repo for this issue, for seeing my issue just pull
, and run npm install
then num run dev
. the project is accessible on localhost:4000
If I break some of your rules please forgive me but guide me how I can settle this issue.
Issue Analytics
- State:
- Created 5 years ago
- Reactions:2
- Comments:23 (8 by maintainers)
Top GitHub Comments
@amerllica Reviewing the information provided, the root of your issue isn’t with right-to-left but with server-side rendering on its own. I would encourage you to simplify the problem as much as possible: for instance, removing the right-to-left handling in the app to better understand what’s going on. You most likely fall into this part: https://material-ui.com/guides/server-rendering/#css-works-on-only-on-first-load.
@basicfu For people who just want to get it working without addressing the root issue, we have a flag for that: https://material-ui.com/customization/css-in-js/#global-css
@nosykretts : I think the problem is not from
next
,jss
ormui
. Your wrapping strategy is problematic as you usedMainLayout
to wrapIndex
, I would not makeMainLayout
wrap theIndex
cuzApp
>Index
> Anything Content ComponentSo
App
is used forRouter + Provider
>Index
is main casePage
Component returned byRouter
>MainLayout
is aLayout
Component along withHeader
&Footer
Components_app.js
index.js