material-ui
material-ui copied to clipboard
Improve Next.js support
Summary ๐ก
This issue will serve as an umbrella for all issues related to Next.js app router integration. It will be easier to have an overview of all the problems in one place.
Done
- [x] Next.js 13 Hydration failed because the initial UI does not match what was rendered on the server #34893
- [x] Next.js 13 example with /app folder #34898
- [x] Next.js 13 compatibility (app directory/server components) #34896
- [x] Add use client directive to all components in a new build step #37503
- [x] Next.js 13 Error during SSR Rendering ReferenceError: defaultStyleFunctionMapping is not defined #34910
- [x] Next.js 13 global styles issue mui/material-ui#37935
- [x] Cover useId Next.js limitation mui/material-ui#38103
- [x] Make Next.js App Router example the default recommended over Next.js Page Router mui/material-ui#38204
- [x] Configure and document modularizeImports for icons mui/material-ui#35450 mui/material-ui#36218
- [x] Create an npm package to abstract APIs for Next.js, solving boilerplate issue mui/material-ui#34575
- [x] Simply the Next.js examples. It should be as simple as possible for developers to start a project and to pull in UI from other places, like from the templates. For example https://github.com/mui/tech-challenge-full-stack/issues/3#issuecomment-1743231738 mui/material-ui#40661
Opportunities to make it even better
- [ ] mui/material-ui#42750
- [ ] mui/material-ui#37934
- [x] mui/material-ui#35993
- [ ] mui/material-ui#38966
- [ ] Migrate Link, remove
legacyBehavior={true}https://github.com/mui/material-ui/issues/38092 - [ ] Add docs on how to integrate the font with the theme https://github.com/mui/material-ui/issues/38082
- [ ] Flatten imports for faster dev server speed mui/material-ui#35840
- [ ] Create guide on how to configure CSP, it's missing in https://mui.com/material-ui/guides/content-security-policy/#how-does-one-implement-csp, related to mui/material-ui#36095
- [ ] Create a @mui/system-nextjs package, move the logic that has noting to do in
@mui/material-nextjs - [ ] Add support for loading indicator between pages when slow network.
https://github.com/mui/material-ui/blob/d37dd5cec56142a16451b9acb6e3d4cd70ac003e/docs/src/modules/components/AppFrame.js#L40-L46
- [ ] Full dark/light mode integration, see the features missing from https://github.com/pacocoursey/next-themes
Not sure if it could be considered to be related to Next13, but I think this issue is also kind of related considering it's about running turbopack within a NextJS 13 application.
https://github.com/mui/material-ui/issues/34910
Not sure if it could be considered to be related to Next13, but I think this issue is also kind of related considering it's about running turbopack within a NextJS 13 application.
https://github.com/mui/material-ui/issues/34910
Thanks, added.
There have been some movement on emotion's side for supporting it in ./app directory. I've forked their stackblitz and added few Material UI components and a custom theme, and it seems like it works ๐ Here is the link to stackblitz. You can check the ./blog route that uses server component.
I would appreciate feedback if someone wants to try it. I personally want to try this on the blog page in the https://mui.com site to verify that everything works. I will create an example project based on the stackblitz later this week.
@mnajdova That's a great news! I hope some day we can use mui in app dir for real :)
I tried your example and I was curious if that works with @mui-treasury/layout I use. Unfortunately some @emotion related errors pop up: TypeError: React.createContext is not a function so I stopped testing this further.
I only modified the root layout and added the package.
Link to my edited stackblitz: https://stackblitz.com/edit/vercel-next-js-zmhzbd
EDIT: Not sure if that helps in any way, maybe there is just a problem in that layout package but I thought I will share it anyway.
After doing some more investigation, I noticed that not all Material UI components can be render as server components. I was lucky in the example that the Typography component was one of them. We will need to revisit this. I will be posting more updates here, or create a new issue that will track the progress on all components.
Based on https://github.com/emotion-js/emotion/issues/2928#issuecomment-1319747902
Note that you should only use Emotion with the so-called client components (we might add "use client"; directive to our files to make this obvious). They can render on the server just fine - for the initial SSRed/streamed HTML but server "refetches" won't render them on the server, they might be rendered on the client when the "refetch" response comes back. This is not Emotion's limitation - it's a limitation for all CSS-in-JS libs like Emotion (including Styled Components, styled-jsx and more)
Looks like the best way forward is to add the "use client" directive to all Material UI components, at least until emotion supports server components.
Looks like the best way forward is to add the "use client" directive to all Material UI components, at least until emotion supports server components.
You don't have to add it to all the client components (although that's certainly the easiest solution here). If you have a set of completely headless components that don't rely on Emotion (nor on Styled-Components or other CSS-in-JS libs) then, to the best of my current understanding, you don't have to add that directive there.
Server Components were designed in a way that is at fundamental odds with CSS-in-JS. So don't expect Emotion (or other popular CSS-in-JS libs) to introduce support for them. They just don't allow us to integrate with them anyhow.
However, that doesn't mean that you can't SSR client components using Emotion. The initial HTML response from the server can freely use the rendered client components. The problem is only when it comes to "server component (re)fetches" (where I'm not even exactly sure how to trigger them, although Next.js probably does those on navigation or smth).
You can still do this:
function ServerComponent({ id }) {
const data = use(fetchData(id))
return <ClientComponent data={data} />
}
Basically, with server components, you might want to move your data-loading concerns out of your client components. That's it. Note that server components are also unique as they support using async/await - so I expect that many people will want to split those concerns anyway (regardless of the fact if they are using CSS-in-JS or not).
Of course, full support of server components would be neat and would make things easier for consumers - and would only require "splitting" if you really want to use async/await, or if you have another reason to do so. But as mentioned - this is probably not going to happen for CSS-in-JS libs as the server component's design doesn't quite allow this.
I've started experimenting with this using a large app at work and there is an initial FOUC before the styles are applied which is proving tricky to debug. I don't see this on the Stackblitz example shared above but I'll see if I can provide an example, although it may be something the emotion team need to worry about.
I can't address something that I can't debug ๐ If you provide a repro case then I will happily jump onto it
You don't have to add it to all the client components (although that's certainly the easiest solution here). If you have a set of completely headless components that don't rely on Emotion (nor on Styled-Components or other CSS-in-JS libs) then, to the best of my current understanding, you don't have to add that directive there.
Totally ๐ MUI Base components that donโt have any interactions could be server components. All Material UI components anyway load emotion, my comment was referring to them.
Basically, with server components, you might want to move your data-loading concerns out of your client components. That's it. Note that server components are also unique as they support using async/await - so I expect that many people will want to split those concerns anyway (regardless of the fact if they are using CSS-in-JS or not).
100% agree with this. I think the misconception comes from the difference that using plain CSS would allow you do convert the UI components too.
I can't address something that I can't debug ๐ If you provide a repro case then I will happily jump onto it
Hey @Andarist @mnajdova here's a repo that shows the FOUC:
https://github.com/robcaldecott/nextjs-mui-app-folder
It's a single page with Container, Grid and two different TextField components. It's using [email protected]. The FOUC happens in both dev and production modes: just refresh the page and you'll see it.
I also see the following console error which may be relevant:
Warning: Each child in a list should have a unique "key" prop. See https://reactjs.org/link/warning-keys for more information.
at style
I am using the EmotionRootStyleRegistry.tsx from the example above.
100% agree with this. I think the misconception comes from the dofference that using plain CSS would allow you do convert the UI components too.
Yeah, this is very well put. It's true that some components with "plain css" can be rendered using server components but the majority of them just can't. From my PoV... I wouldn't like to bother thinking in those terms when writing components in the first place. It's creating a mental overhead - "can I use styles in this server component?". It's much easier to just "forbid" this in the code and make the distinction clear (even with a lint rule or something). Having to restructure your server component with plain CSS just because you want to add some interactivity would also be a chore - it's just, IMHO, easier to start with an explicit split between those concerns and leave server components as something without styles at all.
@robcaldecott this was an interesting case to investigate!
You need to add an explicit <head/> element to your layout to fix this:
diff --git a/app/layout.tsx b/app/layout.tsx
index 9c052a8..68bb86c 100644
--- a/app/layout.tsx
+++ b/app/layout.tsx
@@ -4,6 +4,7 @@ import EmotionRootStyleRegistry from "./EmotionRootStyleRegistry";
export default function RootLayout({ children }: { children: ReactNode }) {
return (
<html>
+ <head></head>
<body>
<EmotionRootStyleRegistry>{children}</EmotionRootStyleRegistry>
</body>
@Andarist OMG thank you so much! I thought the original example had left that in by mistake, ha!
tss-react now provides a setup helper for Next 13 appDir.
If you use it, emotion and consequently MUI will work as well.
tss-reactnow provides a setup helper for Next 13 appDir. If you use it, emotion and consequently MUI will work as well.
@garronej You link navigates to a 404 page. Url was encoded and hash was replaced ;) Working link: https://docs.tss-react.dev/ssr/next.js#app-dir
@barthicus, would you mind creating a code sandbox for the same? I have used the same example mentioned in the document but couldn't SSR the MUI component.
I'm still getting the same error:
error - (sc_server)/node_modules/@mui/base/node/FormControlUnstyled/FormControlUnstyledContext.js (13:54) @ eval
error - TypeError: React.createContext is not a function
at eval (webpack-internal:///(sc_server)/./node_modules/@mui/base/node/FormControlUnstyled/FormControlUnstyledContext.js:48:60)
at (sc_server)/./node_modules/@mui/base/node/FormControlUnstyled/FormControlUnstyledContext.js (/home/e-ubuntu20/Desktop/Experiment/next-13-mui/.next/server/app/page.js:3087:1)
at __webpack_require__ (/home/e-ubuntu20/Desktop/Experiment/next-13-mui/.next/server/webpack-runtime.js:33:42)
at eval (webpack-internal:///(sc_server)/./node_modules/@mui/base/node/FormControlUnstyled/FormControlUnstyled.js:12:58)
at (sc_server)/./node_modules/@mui/base/node/FormControlUnstyled/FormControlUnstyled.js (/home/e-ubuntu20/Desktop/Experiment/next-13-mui/.next/server/app/page.js:3076:1)
at __webpack_require__ (/home/e-ubuntu20/Desktop/Experiment/next-13-mui/.next/server/webpack-runtime.js:33:42)
at eval (webpack-internal:///(sc_server)/./node_modules/@mui/base/node/FormControlUnstyled/index.js:35:51)
at (sc_server)/./node_modules/@mui/base/node/FormControlUnstyled/index.js (/home/e-ubuntu20/Desktop/Experiment/next-13-mui/.next/server/app/page.js:3109:1)
at __webpack_require__ (/home/e-ubuntu20/Desktop/Experiment/next-13-mui/.next/server/webpack-runtime.js:33:42)
at eval (webpack-internal:///(sc_server)/./node_modules/@mui/base/node/index.js:245:52) {
type: 'TypeError',
page: '/'
}
Here is the sandbox for the same: https://codesandbox.io/s/white-meadow-ft6ok7?file=/app/layout.tsx
@ayushrajniwal-jtg There you go, your playground fixed a demo setup
You can't use providers in server components: https://beta.nextjs.org/docs/rendering/server-and-client-components#rendering-third-party-context-providers-in-server-components
Thanks for looking into it @garronej. However, the playground is not working either. Getting the same error as mentioned above
@barthicus, would you mind creating a code sandbox for the same? I have used the same example mentioned in the document but couldn't SSR the MUI component.
I'm still getting the same error:
error - (sc_server)/node_modules/@mui/base/node/FormControlUnstyled/FormControlUnstyledContext.js (13:54) @ eval error - TypeError: React.createContext is not a function at eval (webpack-internal:///(sc_server)/./node_modules/@mui/base/node/FormControlUnstyled/FormControlUnstyledContext.js:48:60) at (sc_server)/./node_modules/@mui/base/node/FormControlUnstyled/FormControlUnstyledContext.js (/home/e-ubuntu20/Desktop/Experiment/next-13-mui/.next/server/app/page.js:3087:1) at __webpack_require__ (/home/e-ubuntu20/Desktop/Experiment/next-13-mui/.next/server/webpack-runtime.js:33:42) at eval (webpack-internal:///(sc_server)/./node_modules/@mui/base/node/FormControlUnstyled/FormControlUnstyled.js:12:58) at (sc_server)/./node_modules/@mui/base/node/FormControlUnstyled/FormControlUnstyled.js (/home/e-ubuntu20/Desktop/Experiment/next-13-mui/.next/server/app/page.js:3076:1) at __webpack_require__ (/home/e-ubuntu20/Desktop/Experiment/next-13-mui/.next/server/webpack-runtime.js:33:42) at eval (webpack-internal:///(sc_server)/./node_modules/@mui/base/node/FormControlUnstyled/index.js:35:51) at (sc_server)/./node_modules/@mui/base/node/FormControlUnstyled/index.js (/home/e-ubuntu20/Desktop/Experiment/next-13-mui/.next/server/app/page.js:3109:1) at __webpack_require__ (/home/e-ubuntu20/Desktop/Experiment/next-13-mui/.next/server/webpack-runtime.js:33:42) at eval (webpack-internal:///(sc_server)/./node_modules/@mui/base/node/index.js:245:52) { type: 'TypeError', page: '/' }Here is the sandbox for the same: https://codesandbox.io/s/white-meadow-ft6ok7?file=/app/layout.tsx
@ayushrajniwal-jtg there you go a working demo:
https://github.com/garronej/mui-next-appdir-demo
https://user-images.githubusercontent.com/6702424/209325604-51fd395c-521e-47b8-99b2-a4f908162047.mov
@ayushrajniwal-jtg there you go a working demo:
https://github.com/garronej/mui-next-appdir-demo
Screen.Recording.2022-12-23.at.12.05.46.mov
I want to use this tss-react solution you provided for a personal project.
However, I tried running your demo and ran into an issue. I see it load initially then it runs into an error when trying to render the client components:

I'm using node v18.12.1 and on windows 10 if that helps. Do you know why I would be seeing this error when I'm using your demo?
Hi @francismanansala,
Have you run the repo exactly as it's provided as per the instruction in the README?
I'm running it on MacOS with node v16.15.1
Hi @francismanansala, Have you run the repo exactly as it's provided as per the instruction in the README?
I'm running it on MacOS with node v16.15.1
Yeah, I've followed the README but used npm instead of yarn. I tried with node 16 as well and I ran into this same issue. I don't think yarn would make a difference
[edited] I tried it with yarn and it made no difference
[edited again] I tried this on a MacOS and it works.
There must be something wrong with my windows machine. Itโs my first time using nextjs, and mui on it so maybe I havenโt set it up correctly.
[edited once again] I found this stack overflow post: https://stackoverflow.com/questions/74832268/typeerror-cannot-read-properties-of-undefined-reading-call-on-next-js. Downgraded to 13.0.6 on windows and it resolved my issue. Possibly a bug in 13.0.7 related to the macOS file system not being case-sensitive but other OS are.
@francismanansala Thanks for taking the time to share the result of your investigations!
@Andarist OMG thank you so much! I thought the original example had left that in by mistake, ha!
That did not change anything, the example still causes FOUC, can you provide a repo where this actually works, please?
https://github.com/garronej/mui-next-appdir-demo
Epic work @garronej!
The bigger question for mui and joy is however how to support RCS in the future. Obviously streamable react server components and concurrent rendering is young and it was only recently mui opted in to emotion on large scale.
Thanks @andriijas,
The bigger question for mui and joy is however how to support RCS in the future. Obviously streamable react server components and concurrent rendering is young and it was only recently mui opted in to emotion on large scale.
See this message and and this other one from Andarist.
https://github.com/garronej/mui-next-appdir-demo
have you tried with nested layouts and redux as top provider?
Since writing a set of wrappers for all MUI components that include the use client directive appears to be the solution most people are going with, has anyone produced such a wrapper library yet? If not I may post one for people to use until there is direct 'splitting' support.
Hello @mnajdova and @Andarist,
I come with bad news ๐
AppDir + emotion is broken.
Hydration errors happen randomly in production (never in dev mode).
After about 50+ hours of thorough investigation, my conclusion: it's a Next.js bug.
I've created a reproduction repo and opened an issue.
If you guys could confirm my conclusions that would help a lot get it noticed by Vercel.
Thanks in advance!
TL;DR: If you start using MUI in an AppDir setup today you'll probably end up with random production errors that breaks the interactivity of the whole app (it's not a warning that can be ignored).
PS: Thank you @Andarist for recommending Replay.io. <3