Styles appear for maybe 50ms and disappear in the below code in this SSR app. I'm curious what could be causing that.
// This component is a child of index.tsx in the /pages folder
<Button
color="primary"
variant="outlined"
size="large"
>Test Button</Button>
After the styles disappear a plain HTML button is left.
I believe Next.js is causing this. I checked the Next.js file and have added the next/babel loader to .babelrc. Other than that I only saw this other relevant change. This is in /pages/_document.js:
MyDocument.getInitialProps = async ctx => {
const sheets = new MuiServerStyleSheets();
const originalRenderPage = ctx.renderPage;
ctx.renderPage = () =>
originalRenderPage({
enhanceApp: App => props => sheets.collect(<App {...props} />),
});
const initialProps = await Document.getInitialProps(ctx);
return {
...initialProps,
// Styles fragment is rendered after the app and page rendering finish.
styles: [...React.Children.toArray(initialProps.styles), sheets.getStyleElement()],
};
};
Things done to attempt to resolve
No change to issue.
No change.
Theory
I think there is a server side problem. Client and server should be on the same machine, localhost. That would explain the correct initial result (client initial UI) then being overridden by a server update.
Update 1
Forgot to mention that I did update /pages/_app.js
too. Here's the updated portion:
class MyApp extends App {
componentDidMount() {
// Remove the server-side injected CSS.
const jssStyles = document.querySelector('#jss-server-side');
if (jssStyles && "parentElement" in jssStyles) {
(jssStyles.parentElement as HTMLElement).removeChild(jssStyles);
}
}
The root cause of this error for me, was that the classes generated during server side rendering of the document, don't match the styles that are generated after hydration.
One way to fix this, is to force a rerender after hidration.
One way to do this, is to update the key prop on your component.
// inside your component
const [key, setKey] = React.useState(0);
React.useEffect(() => {
setKey(1);
}, []);
return (<MyComponent key={}key />)
My full _app.tsx
file:
import React from 'react';
import {
ThemeProvider,
createGenerateClassName,
StylesProvider
} from '@material-ui/core/styles';
import CssBaseline from '@material-ui/core/CssBaseline';
import { darkTheme } from '../theme';
const generateClassName = createGenerateClassName({
productionPrefix: 'myclasses-'
});
export default function MyApp(props) {
const { Component, pageProps } = props;
const [key, setKey] = React.useState(0);
React.useEffect(() => {
setKey(1);
}, []);
React.useEffect(() => {
const jssStyles = document.querySelector('#jss-server-side');
if (jssStyles) {
jssStyles.parentElement.removeChild(jssStyles);
}
}, []);
return (
<StylesProvider key={key} generateClassName={generateClassName}>
<React.Fragment>
<ThemeProvider theme={darkTheme}>
<CssBaseline />
<Component {...pageProps} />
</ThemeProvider>
</React.Fragment>
</StylesProvider>
);
}
TLDR; Make sure to set NODE_ENV to production if you are running a production build, eg: NODE_ENV=production npm start
For me, this was happening only on my machine when creating a production build with npm run build
and then running it with npm start
.
It was strange why server-rendered responses used development style classes such as makeStyles-root-123
instead of jss123
as explained by https://material-ui.com/styles/advanced/#class-names.
Clearly, some part of the server acted as we were still in a development environment. To fix this I tried starting the production build with NODE_ENV
set to "production" and the issue was gone.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With