Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[material-ui-icons] load-order problem with SSR and ES6 use of material-ui; needs ES variant? #10649

Closed
estaub opened this issue Mar 14, 2018 · 14 comments
Labels
component: SvgIcon The React component. package: styles Specific to @mui/styles. Legacy package, @material-ui/styled-engine is taking over in v5.

Comments

@estaub
Copy link
Contributor

estaub commented Mar 14, 2018

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

@oliviertassinari oliviertassinari added duplicate This issue or pull request already exists core and removed duplicate This issue or pull request already exists labels Mar 14, 2018
@oliviertassinari
Copy link
Member

oliviertassinari commented Mar 14, 2018

@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 #10212 (comment), 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.

@estaub
Copy link
Contributor Author

estaub commented Mar 15, 2018

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!

@CocoJr
Copy link

CocoJr commented Jul 28, 2018

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 ?

@oliviertassinari
Copy link
Member

@CocoJr You need to use the JssProvider component to share the same JSS instance between all the mounted React component.

@CocoJr
Copy link

CocoJr commented Jul 28, 2018

@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 ....

@oliviertassinari
Copy link
Member

I do just this update and all works perfectly ....

I'm not surprised. Are we good then?

@CocoJr
Copy link

CocoJr commented Jul 28, 2018

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 ....

@oliviertassinari
Copy link
Member

oliviertassinari commented Sep 1, 2018

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 #10649 (comment), it only works if you don't rely on third-party libraries using Material-UI.

@dantman
Copy link
Contributor

dantman commented Sep 1, 2018

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/.

@oliviertassinari
Copy link
Member

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!

@dantman
Copy link
Contributor

dantman commented Sep 1, 2018

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.

@alex996
Copy link

alex996 commented Sep 3, 2018

@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?

@dantman
Copy link
Contributor

dantman commented Sep 3, 2018

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.

@oliviertassinari
Copy link
Member

oliviertassinari commented Sep 4, 2018

@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.

@oliviertassinari oliviertassinari added the status: waiting for maintainer These issues haven't been looked at yet by a maintainer label Dec 20, 2022
@zannager zannager added component: SvgIcon The React component. package: styles Specific to @mui/styles. Legacy package, @material-ui/styled-engine is taking over in v5. and removed status: waiting for maintainer These issues haven't been looked at yet by a maintainer labels Jan 9, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
component: SvgIcon The React component. package: styles Specific to @mui/styles. Legacy package, @material-ui/styled-engine is taking over in v5.
Projects
None yet
Development

No branches or pull requests

6 participants