material-ui-icons uses material-ui/SvgIcon, which uses material-ui/styles/withStyles, which creates a static generateClassNames, resetting the static rule counter.
In an app that is otherwise using ES6 material-ui, including material-ui/**es**/styles/withStyles, this results in the counter being reset. If that happens before any JssProvider is set up, all good. But if it happens afterward, as is likely with chunked production client code, it resets the rule counter late, resulting in className skewage in hydration of the SSR rendering.
A workaround in client code is to force early loading of material-ui/styles/withStyles in the browser, e.g., in TypeScript:
import {default as wrongWithStyles} from 'material-ui/styles/withStyles'
const _fooWrongStyles = wrongWithStyles
@estaub This issue has already been discussed in #10212. The status quo is, only use the /es folder if you don't rely on third-party libraries and you don't want to have Material-UI transpiled down to ES2015.
A possible good solution would be to implement https://github.com/mui-org/material-ui/issues/10212#issuecomment-364704782, but the risk is high compared to the low output.
I'm not sure what's your use case for using the /es folder. #8473 can temporarily help but it will be soon removed in #9469.
I'm using /es to shrink material-ui's footprint using webpack. If there's some other way to do that, that doesn't involve dragging babel into my toolchain, I'm all ears!
Hey!
Any news about this issue? I use material-ui-icons in my navbar, and so i have a server side rendering issues:
Warning: Prop className did not match. Server: "MuiSvgIcon-root-62" Client: "MuiSvgIcon-root-1"
And my design is fucked off ><
I import all from /es, same for withStyles.
The only way to fix that for me is to update the createSvgIcon.js to use the /es SvgIvon. This is really strange because i import from material-ui/icons/es/* so why this function don't use the es SvgIcon ?
@CocoJr You need to use the JssProvider component to share the same JSS instance between all the mounted React component.
@oliviertassinari look my edit :)
@material-ui/icons/es/utils/createSvgIcon.js has one bad import: import SvgIcon from '@material-ui/core/SvgIcon'; instead of import SvgIcon from '@material-ui/core/es/SvgIcon';
I do just this update and all works perfectly ....
I do just this update and all works perfectly ....
I'm not surprised. Are we good then?
yes, i have an other "bugs" of SSR:
Warning: Did not expect server HTML to contain a <div> in <main>.
But this is linked to react only :) My design works fine and no mismatch bewteen my SSR and my client. I don't know how to fix that in your repo :/
EDIT: All is fixed with that. I use temporally sed -i 's/material-ui\/core\/SvgIcon/material-ui\/core\/es\/SvgIcon/' 'node_modules/@material-ui/icons/es/utils/createSvgIcon.js' to fix it inside my project ....
A possible good solution would be to implement #10212 (comment), but the risk is high compared to the low output.
Giving more thought to the issue, I don't think that we can move forward with this approach. It would require the duplication of the icons, something that makes a 30MB package a 50MB package with 10k files. It's often making yarn timeout.
The only path forward I can think of is publish a second package like @material-ui/icons-es. But following https://github.com/mui-org/material-ui/issues/10649#issuecomment-373179582, it only works if you don't rely on third-party libraries using Material-UI.
The only path forward I can think of is publish a second package like @material-ui/icons-es. But following #10649 (comment), it only works if you don't rely on third-party libraries using Material-UI.
I don't think @material-ui/icons/es and @material-ui/icons-es are any different in that regard. I use the MUI es/ folder fine even with 3rd party libraries using a WebPack config to make everything use es/.
I use the MUI es/ folder fine even with 3rd party libraries using a WebPack config to make everything use es/.
@dantman Oh, this is smart. Thanks for sharing this great workaround!
Here's the code for lodash-es and @material-ui/es.
// Force imports of packages like lodash to use the *-es package variant
new webpack.NormalModuleReplacementPlugin(/^lodash(\/|$)/, resource => {
resource.request = resource.request.replace(
/^(@[^/+]+\/[^/+]+|[^/+]+)(\/.*)?$/,
'$1-es/$2'
);
}),
// Force imports of packages like @material-ui/core to use the /es versions
new webpack.NormalModuleReplacementPlugin(
/^@material-ui\/core(\/|$)/,
resource => {
resource.request = resource.request.replace(
/^(@[^/+]+\/[^/+]+|[^/+]+)(?:\/es)?(\/.*)?$/,
'$1/es$2'
);
}
),
It already works with lodash-es, so IMHO an @material-ui/icons-es would probably work out fairly well.
@dantman Sure, but for MUI, it doesn't address the issue of shipping both ES5 and ES6 versions of icons, which leads to increased lib size, and opens doors for duplication. And the latter only unless you rewrite the import paths explicitly, as you did, but even then you're targeting ES6. This won't work if you need to support IE11, or else you'd have to transpile MUI with preset-env yourself.
@oliviertassinari From what I understand, /es was removed from @material-ui/icons for the above reasons, but was kept in @material-ui/core, which I find strange. Is there plans to keep it there or outsource to a separate package likewise? What's the reasoning for one vs. the other?
Sure, but for MUI, it doesn't address the issue of shipping both ES5 and ES6 versions of icons, which leads to increased lib size, and opens doors for duplication.
If the ES6 code is in a separate -es package then there's no duplication in the @material-ui/icons package. And if you use npm's resolutions ES6 users may even be able to avoid installing the ES5 package indirectly.
And the latter only unless you rewrite the import paths explicitly, as you did, but even then you're targeting ES6. This won't work if you need to support IE11, or else you'd have to transpile MUI with preset-env yourself.
We target IE11. We do transpile MUI using preset-env. And we have separate modern and legacy bundles. So the modern bundle gets few polyfills and transforms, making it much smaller. This is still advantageous even for legacy bundle that supports IE11 because the es/ version of MUI transpiled in our build system is still smaller than the ES5 version of MUI outside the es/ folder.
@alex996 The /es folder was removed from the icons because of the overhead. We don't have 5k files on the core side. The tradeoff is acceptable. I don't think that the JavaScript community has yet found a nice way to release multiple versions of a library. You have to make tradeoffs between package side, modules duplication, bundle size bloat, etc. The harderst part is around handling projects build on top of Material-UI. Using aliases to solve the problem is something I never thought of until now. It's well supported by the bundlers.
For the icons, tree shaking might never be an option because of the 5k imports in the index, making it too slow.
Most helpful comment
Here's the code for
lodash-esand@material-ui/es.It already works with lodash-es, so IMHO an
@material-ui/icons-eswould probably work out fairly well.