Closed MartinXPN closed 1 year ago
Also next/link
no longer require <a>
as a child of Link
component
Check https://nextjs.org/blog/next-13#nextlink
Code example affected https://github.com/mui/material-ui/blob/012c95f917a5db74fd530c63260616d3c3803cd9/examples/nextjs-with-typescript/src/Link.tsx#L8-L37
add prop legacyBehavior
to next/link
component
Another workaround for Link
might be removing the Anchor
and adding ref
and ...other
right to the NextLink
.
But this might break some edge case that I'm not familiar with.
Probably for the full compatibility of Mui 5 with NextJs 13, Emotion must have support for React Server Components and Concurrent Rendering.
This already has been requested at Emotion's repo: https://github.com/emotion-js/emotion/issues/2928
Is there any workaround that would enable us to use the app
directory now without waiting for emotion
and MUI
library updates?
Duplicate of https://github.com/mui/material-ui/issues/34893
@mnajdova I don't think this is a duplicate of #34893 Here we ask for a working example app that would work with Next.js 13 (similar to the previous Next.js version https://github.com/mui/material-ui/blob/master/examples/nextjs-with-typescript ), while the other issue reports a bug with hydration.
I think those are completely different requests/issues.
Temporary workaround
add prop
legacyBehavior
tonext/link
component
I thought this commit would be a good read at least for the next/link
issues. But there would still be minor ts error around
interface NextLinkComposedProps
extends Omit<React.AnchorHTMLAttributes<HTMLAnchorElement>, "href">,
Omit<NextLinkProps, "href" | "as" | "onClick" | "onMouseEnter"> {
to: NextLinkProps["href"];
linkAs?: NextLinkProps["as"];
}
I think the NextJS examples needs to be split into two separate examples: pages
and app
folder based. For now only pages
will work due to the underlying Emotion issue https://github.com/vercel/next.js/issues/41994
Wouldnt it make sense to pin the versions of the used libs at least to the major versions?
Wouldnt it make sense to pin the versions of the used libs at least to the major versions?
Locally in your projects yes. In our examples, we intentionally use latest
, so that we can catch problems immediately when they happen
For next/link
I just opened #34970 that sets up support for the legacyBehaviour
prop to fix existing errors in the examples
I think one option to properly migrate to v13 would be to remove the need for NextLinkComposed
and just show examples of doing <MuiLink component={NextLink}/>
. This would require some changes to docs as well to support both the v12 and v13 routers
And as for @next/font
I tried out the package in a few of my projects and I think I landed on a fairly good way to use it with MUI. Happy to hear other takes on it though!
@PetroSilenius I just got this link from my colleague: https://mui.com/material-ui/guides/routing/#global-theme-link
I'm mad I didn't find this earlier fix every use of an MUI Link
or component based on ButtonBase
. No need to add the NextLink
as a component to every link and button, just override the default linkComponent
for those components and your set.
Yea the global theme Link is a great tool! It might be a bit opionated for the nextjs examples though as there are some cases where NextLink doesn't behave as needed for example with external links with a certain target. Could point it up better in the docs thoughππ»
@PetroSilenius I'm going to add a check if the href is relative, if so use NextLink otherwise use an a. moving that logic to the theme saves a lot of boilerplate.
Can workaround the createContext is undefined
error because the stateful component is rendered on the server by wrapping it:
"use client" // <--- this
import {Button} from "@mui/material"
And then use this new file to import the component.
I don't like it but if maybe someone has no choice and has to move it NOW to next 13, then he could re-export it this way... There are sure way make it less of a pain to undo later.
I work on the following boilerplate, and had to self-solve most of these issues.
https://github.com/Rethunk-Tech/nextjs-boilerplate
Is there any more progress on this issue I should be aware of?
Is it better to track #34905 instead?
Is it better to track https://github.com/mui/material-ui/issues/34905 instead?
Yep, that's better. We already have an next.js example using 13, but it doesn't cover the experimental app
folder.
A related discussion is happening over in the Emotion js repo: https://github.com/emotion-js/emotion/issues/2928
Hi, I'm just setting up a new website using Next.js and MUI. I really wanted to try the app/
directory out but found out it doesn't work the hard way.
I see #34905 and #34896, but I'd like to ask anyway - what's the progress on this? An example app would really come in handy, even if it contained workarounds that are planned to be removed in time. Also, on this note - is there somewhere a list of workarounds currently needed to make this work? Or the recommended way is not to use app/
directory for now?
@rtrembecky it definitely works with some workarounds. See the emotion thread above for some hints.
Hi, I'm just setting up a new website using Next.js and MUI. I really wanted to try the
app/
directory out but found out it doesn't work the hard way.I see #34905 and #34896, but I'd like to ask anyway - what's the progress on this? An example app would really come in handy, even if it contained workarounds that are planned to be removed in time. Also, on this note - is there somewhere a list of workarounds currently needed to make this work? Or the recommended way is not to use
app/
directory for now?
Hi, did you find any resources for those workarounds?
up
Hi, I'm just setting up a new website using Next.js and MUI. I really wanted to try the
app/
directory out but found out it doesn't work the hard way. I see #34905 and #34896, but I'd like to ask anyway - what's the progress on this? An example app would really come in handy, even if it contained workarounds that are planned to be removed in time. Also, on this note - is there somewhere a list of workarounds currently needed to make this work? Or the recommended way is not to useapp/
directory for now?Hi, did you find any resources for those workarounds?
Hey, did you find any solutions?
Hi, did you find any resources for those workarounds?
Hey, did you find any solutions?
@cristianbuta @danielcolgan hey guys π yes, but I was lazy to report back here, though I expected such questions to appear π
it does indeed work and this mentioned emotion issue thread is really pretty insightful: https://github.com/emotion-js/emotion/issues/2928 - there are several examples, I picked the lowest-code one, find the usage below.
(also here are some useful StackBlitzes from @karlhorky, though more related to the emotion
compiler option: https://github.com/vercel/next.js/issues/41994#issuecomment-1445061359)
let me try to summarize my setup (using next 13.2.1, so with new metadata structure = no <head />
tag). note the "use client"
directives - these are telling next not to treat these files as server components (BTW: server components are just a new optimization for the app directory, you are perfectly fine not using them). these directives will be necessary for all the files with components using MUI. my example uses the tss-react
package just to use the NextAppDirEmotionCacheProvider
utility they have π but you can also copy its code.
// src/app/layout.tsx
import { Metadata } from "next/dist/lib/metadata/types/metadata-interface"
import { MuiSetup } from "./MuiSetup"
export const metadata: Metadata = {
title: "My title",
description: "My description",
}
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en">
<body>
<MuiSetup>{children}</MuiSetup>
</body>
</html>
)
}
// src/app/theme.ts
import { createTheme } from "@mui/material"
export const theme = createTheme()
// src/app/MuiSetup.tsx
"use client"
import { CssBaseline, ThemeProvider } from "@mui/material"
import { ReactNode } from "react"
import { NextAppDirEmotionCacheProvider } from "tss-react/next/appDir"
import { theme } from "./theme"
type Props = {
children: ReactNode
}
export const MuiSetup = ({ children }: Props) => {
return (
<>
<CssBaseline />
{/* MUI (but actually underlying Emotion) isn't ready to work with Next's experimental `app/` directory feature.
I'm using the lowest-code approach suggested by this guy here: https://github.com/emotion-js/emotion/issues/2928#issuecomment-1386197925 */}
<NextAppDirEmotionCacheProvider options={{ key: "css" }}>
<ThemeProvider theme={theme}>{children}</ThemeProvider>
</NextAppDirEmotionCacheProvider>
</>
)
}
// src/app/page.tsx
import { Home } from "./Home"
export default function Page() {
return <Home />
}
// src/app/Home.tsx
"use client"
import { Stack, Typography } from "@mui/material"
export const Home = () => (
<Stack sx={{ minHeight: "100vh", padding: "64px" }}>
<Stack sx={{ flexGrow: 1, justifyContent: "center", alignItems: "center" }} component="main">
<Typography variant="h5">main content</Typography>
</Stack>
<Stack direction="row" sx={{ justifyContent: "space-between" }} component="footer">
<Typography>footer</Typography>
<Typography>stuff</Typography>
</Stack>
</Stack>
)
these 5 files are all the files I have in the src/app/
folder (ok, maybe plus newly moved favicon.ico
) and there's no change needed elsewhere.
I am using next-themes and Tailwind to add a dark mode option.
Based on the code provided by @rtrembecky I made this example:
// tailwind.config.js
/** @type {import('tailwindcss').Config} */
const config = {
darkMode: "class",
content: ["./src/app/**/*.tsx"],
theme: {
extend: {},
},
variants: {},
plugins: [],
};
module.exports = config;
/* src/app/globals.css */
@tailwind components;
@tailwind utilities;
// src/app/layout.tsx
import "./globals.css";
import Providers from "./Providers";
export const metadata = {
title: "My title",
description: "My description",
};
const RootLayout = ({ children }: { children: React.ReactNode }) => (
// suppressHydrationWarning is for next-themes - see: https://github.com/pacocoursey/next-themes#with-app
<html lang="en" suppressHydrationWarning>
<head />
<body>
<Providers>{children}</Providers>
</body>
</html>
);
export default RootLayout;
// src/app/theme.ts
export const DEFAULT_THEME: "dark" | "light" = "dark";
export const getOtherTheme = (theme: string | undefined): "dark" | "light" => {
switch (theme) {
case "dark":
return "light";
case "light":
return "dark";
case "system":
default:
return DEFAULT_THEME;
}
};
// src/app/Providers.tsx
"use client";
import { createTheme as createMuiTheme, ThemeProvider as MuiThemeProvider } from "@mui/material";
import { ThemeProvider, useTheme } from "next-themes";
import { NextAppDirEmotionCacheProvider } from "tss-react/next/appDir";
import { DEFAULT_THEME } from "./theme";
const MuiProvider = ({ children }: { children: React.ReactNode }) => {
const { theme: themeState } = useTheme();
const themeName = themeState === "dark" || themeState === "light" ? themeState : DEFAULT_THEME;
const theme = createMuiTheme({ palette: { mode: themeName } });
return (
// CssBaseline causes the theme switch to stop working
<NextAppDirEmotionCacheProvider options={{ key: "css" }}>
<MuiThemeProvider theme={theme}>{children}</MuiThemeProvider>
</NextAppDirEmotionCacheProvider>
);
};
const NextThemeProvider = ({ children }: { children: React.ReactNode }) => (
// Separate next-themes Provider from MUI, so is does not get rerendered on theme switch
<ThemeProvider attribute="class" defaultTheme={DEFAULT_THEME}>
{children}
</ThemeProvider>
);
const Providers = ({ children }: { children: React.ReactNode }) => (
<NextThemeProvider>
<MuiProvider>{children}</MuiProvider>
</NextThemeProvider>
);
export default Providers;
// src/app/page.tsx
import MuiContent from "./MuiContent";
import ThemeButton from "./ThemeButton";
const Index = () => (
<>
<ThemeButton />
<MuiContent />
</>
);
export default Index;
// src/app/ThemeButton.tsx
"use client";
import { useTheme } from "next-themes";
import { useEffect, useState } from "react";
import { DEFAULT_THEME, getOtherTheme } from "./theme";
const ThemeButton = () => {
const { theme: themeState, setTheme } = useTheme();
const [themeName, setThemeName] = useState(DEFAULT_THEME);
useEffect(() => setThemeName(getOtherTheme(themeState)), [themeState]);
return (
<button type="button" onClick={() => setTheme(getOtherTheme(themeState))}>
{`Activate ${themeName} Theme`}
</button>
);
};
export default ThemeButton;
// src/app/MuiContent.tsx
"use client";
import { Stack, Typography } from "@mui/material";
import { useTheme } from "next-themes";
import { useEffect, useState } from "react";
import { DEFAULT_THEME, validThemeOrDefault } from "@/theme";
const MuiContent = () => {
const { theme } = useTheme();
const [themeName, setThemeName] = useState(DEFAULT_THEME);
useEffect(() => setThemeName(validThemeOrDefault(theme)), [theme]);
return (
<Stack sx={{ minHeight: "100vh", padding: "64px" }}>
<Stack sx={{ flexGrow: 1, justifyContent: "center", alignItems: "center" }} component="main">
<Typography variant="h5">main content with Theme: {`${themeName}`}</Typography>
</Stack>
<Stack direction="row" sx={{ justifyContent: "space-between" }} component="footer">
<Typography>footer</Typography>
<Typography>stuff</Typography>
</Stack>
</Stack>
);
};
export default MuiContent;
I tested @rtrembecky's sample code, and it worked, but then I tried a different way of using MUI components in application pages. The layout.tsx
, theme.ts
and MuiSetup.tsx
files remain as the originals, so I'll only show the different files.
This file exports all Material content but has a "use client" line, which forces all components to be client side.
// src/components/@mui/material/index.tsx
'use client';
export * from '@mui/material';
On the page we can import the components as in Material. It can still be a server component, and we also don't need to group the material components into a different component with the "use client" line.
// src/app/page.tsx
import { Alert, AlertTitle, Button, Container } from '../components/@mui/material';
export default function Home() {
return (
<Container>
<Alert severity="error">
<AlertTitle>Error</AlertTitle>
This is an error alert with title β check it out!
</Alert>
<Alert severity="success">
<AlertTitle>Success</AlertTitle>
This is a success alert with title β check it out!
</Alert>
<Button variant="contained">
Contained
</Button>
</Container>
);
}
We can also create specific files to export each material component, which reduces page load time by about 0.4s in my tests. Each file looks like this:
// src/components/@mui/material/Alert.tsx
'use client';
import Alert from '@mui/material/Alert';
export default Alert;
And we can import the component like in material with:
import Alert from '@/components/@mui/material/Alert';
The code seems to work fine. I don't know if this decreases performance as I am creating server components which in some cases may have many client components inside it. If anyone has an opinion on this, I'd like to know.
I tested @rtrembecky's sample code, and it worked, but then I tried a different way of using MUI components in application pages. The
layout.tsx
,theme.ts
andMuiSetup.tsx
files remain as the originals, so I'll only show the different files.This file exports all Material content but has a "use client" line, which forces all components to be client side.
// src/components/@mui/material/index.tsx 'use client'; export * from '@mui/material';
On the page we can import the components as in Material. It can still be a server component, and we also don't need to group the material components into a different component with the "use client" line.
// src/app/page.tsx import { Alert, AlertTitle, Button, Container } from '../components/@mui/material'; export default function Home() { return ( <Container> <Alert severity="error"> <AlertTitle>Error</AlertTitle> This is an error alert with title β check it out! </Alert> <Alert severity="success"> <AlertTitle>Success</AlertTitle> This is a success alert with title β check it out! </Alert> <Button variant="contained"> Contained </Button> </Container> ); }
We can also create specific files to export each material component, which reduces page load time by about 0.4s in my tests. Each file looks like this:
// src/components/@mui/material/Alert.tsx 'use client'; import Alert from '@mui/material/Alert'; export default Alert;
And we can import the component like in material with:
import Alert from '@/components/@mui/material/Alert';
The code seems to work fine. I don't know if this decreases performance as I am creating server components which in some cases may have many client components inside it. If anyone has an opinion on this, I'd like to know.
@renanrms this worked great! thanks for the suggestion.
I used the simple // src/components/@mui/material/index.tsx
example you gave for now but will most likely move to the more performant solution you provided with making the individual files when the app gets larger.
thanks again π and thanks @rtrembecky for the original suggestion / workaround!
@Cielquan Thanks for the example. Did you encounter this error? For some reason when generating the theme based on the selected theme name (light/dark) it fails to match the classnames between re-renders.
Also, the CssBaseline is required, otherwise styles such as typography are broken. Did you manage to find a solution for that?
@arobert93,
TL;DR
example repo I created link that fixes issues with CssBaseLine
and removes need for next-themes
based on a culmination of posts. Details and links below
edit the repo has been updated to include next-themes
to prevent flashing on load.
// suppressHydrationWarning is for next-themes - see: https://github.com/pacocoursey/next-themes#with-app <html lang="en" suppressHydrationWarning>
Looks like someone else had an idea around cookie usage over on your other post link
Also, the CssBaseline is required, otherwise styles such as typography are broken
I experienced the same. I found this link to a codesandbox that used some experimental features but the CodeSB didnt work out of the box (outdated dependency issues).
I forked it and created a repo you can check out link. It is basic and just uses the system color scheme (no theme changing button etc) but i didnt need to include next-themes
, it uses all of the default out of the box MUI theme switching logic.
I did not update the example to use @renanrms idea but the example repo should be enough for a jumping off point, will try to update it with all the bells and whistles at a later point
hope this helps π
I just did a little testing and tried to get all my prior used providers to work, when I came up with my solution above. I did not notice any issues with the Typography component yet.
Unfortunately I will have no time to dig into the topic again more deeply for some weeks. But I guess @dougiefresh49 answer can solve the issue, which is nice. π
Hi Since NextJS 13.4 is now released, which claims the app router directory to be stable, when can we expect an official example code to use MUI with NextJS for server components?
Good day everyone! I am also curious to see some examples on how to transition from pages to app directory. I am working on a project using a template from https://minimals.cc.
Currently, in the pages folder, my _app.tsx, _document.tsx, and index.tsx appears as follows:
// _app.tsx
// i18n
import '../locales/i18n';
// scroll bar
import 'simplebar-react/dist/simplebar.min.css';
// lightbox
import 'yet-another-react-lightbox/styles.css';
import 'yet-another-react-lightbox/plugins/captions.css';
import 'yet-another-react-lightbox/plugins/thumbnails.css';
// map
import 'mapbox-gl/dist/mapbox-gl.css';
// editor
import 'react-quill/dist/quill.snow.css';
// slick-carousel
import 'slick-carousel/slick/slick.css';
import 'slick-carousel/slick/slick-theme.css';
// lazy image
import 'react-lazy-load-image-component/src/effects/blur.css';
// ----------------------------------------------------------------------
import { CacheProvider, EmotionCache } from '@emotion/react';
// next
import { NextPage } from 'next';
import Head from 'next/head';
import { AppProps } from 'next/app';
// redux
import { Provider as ReduxProvider } from 'react-redux';
// @mui
import { LocalizationProvider } from '@mui/x-date-pickers';
import { AdapterDateFns } from '@mui/x-date-pickers/AdapterDateFns';
// redux
import { store } from '../redux/store';
// utils
import createEmotionCache from '../utils/createEmotionCache';
// theme
import ThemeProvider from '../theme';
// locales
import ThemeLocalization from '../locales';
// components
import { StyledChart } from '../components/chart';
import ProgressBar from '../components/progress-bar';
import SnackbarProvider from '../components/snackbar';
import { MotionLazyContainer } from '../components/animate';
import { ThemeSettings, SettingsProvider } from '../components/settings';
// Check our docs
// https://docs.minimals.cc/authentication/ts-version
import { AuthProvider } from '../auth/JwtContext';
// import { AuthProvider } from '../auth/Auth0Context';
// import { AuthProvider } from '../auth/FirebaseContext';
// import { AuthProvider } from '../auth/AwsCognitoContext';
// ----------------------------------------------------------------------
const clientSideEmotionCache = createEmotionCache();
type NextPageWithLayout = NextPage & {
getLayout?: (page: React.ReactElement) => React.ReactNode;
};
interface MyAppProps extends AppProps {
emotionCache?: EmotionCache;
Component: NextPageWithLayout;
}
export default function MyApp(props: MyAppProps) {
const { Component, pageProps, emotionCache = clientSideEmotionCache } = props;
const getLayout = Component.getLayout ?? ((page) => page);
return (
<CacheProvider value={emotionCache}>
<Head>
<meta name="viewport" content="initial-scale=1, width=device-width" />
</Head>
<AuthProvider>
<ReduxProvider store={store}>
<LocalizationProvider dateAdapter={AdapterDateFns}>
<SettingsProvider>
<MotionLazyContainer>
<ThemeProvider>
<ThemeSettings>
<ThemeLocalization>
<SnackbarProvider>
<StyledChart />
<ProgressBar />
{getLayout(<Component {...pageProps} />)}
</SnackbarProvider>
</ThemeLocalization>
</ThemeSettings>
</ThemeProvider>
</MotionLazyContainer>
</SettingsProvider>
</LocalizationProvider>
</ReduxProvider>
</AuthProvider>
</CacheProvider>
);
}
// _document.tsx
import * as React from 'react';
// next
import Document, { Html, Head, Main, NextScript } from 'next/document';
// @emotion
import createEmotionServer from '@emotion/server/create-instance';
// utils
import createEmotionCache from '../utils/createEmotionCache';
// theme
import palette from '../theme/palette';
import { primaryFont } from '../theme/typography';
// ----------------------------------------------------------------------
export default class MyDocument extends Document {
render() {
return (
<Html lang="en" className={primaryFont.className}>
<Head>
<meta charSet="utf-8" />
<link rel="manifest" href="/manifest.json" />
{/* PWA primary color */}
<meta name="theme-color" content={palette('light').primary.main} />
{/* Favicon */}
<link rel="apple-touch-icon" sizes="180x180" href="/favicon/apple-touch-icon.png" />
<link rel="icon" type="image/png" sizes="32x32" href="/favicon/favicon-32x32.png" />
<link rel="icon" type="image/png" sizes="16x16" href="/favicon/favicon-16x16.png" />
{/* Emotion */}
<meta name="emotion-insertion-point" content="" />
{(this.props as any).emotionStyleTags}
{/* Meta */}
<meta
name="description"
content="Alert 360 Home Security Systems up to 25% off RETAIL! Save big on Alarm Monitoring starting at $15.95 & Top-Rated Security Cameras!"
/>
<meta name="keywords" content="home,security,alarm,system,wireless,monitoring,cameras" />
<meta name="author" content="Alert 360" />
</Head>
<body>
<Main />
<NextScript />
</body>
</Html>
);
}
}
// ----------------------------------------------------------------------
MyDocument.getInitialProps = async (ctx) => {
const originalRenderPage = ctx.renderPage;
const cache = createEmotionCache();
const { extractCriticalToChunks } = createEmotionServer(cache);
ctx.renderPage = () =>
originalRenderPage({
enhanceApp: (App: any) =>
function EnhanceApp(props) {
return <App emotionCache={cache} {...props} />;
},
});
const initialProps = await Document.getInitialProps(ctx);
const emotionStyles = extractCriticalToChunks(initialProps.html);
const emotionStyleTags = emotionStyles.styles.map((style) => (
<style
data-emotion={`${style.key} ${style.ids.join(' ')}`}
key={style.key}
// eslint-disable-next-line react/no-danger
dangerouslySetInnerHTML={{ __html: style.css }}
/>
));
return {
...initialProps,
emotionStyleTags,
};
};
// index.tsx
// next
import Head from 'next/head';
// @mui
import { Box } from '@mui/material';
// layouts
import MainLayout from '../layouts/main';
// components
import ScrollProgress from '../components/scroll-progress';
// sections
import {
HomeHero,
HomeMinimal,
HomeDarkMode,
HomeLookingFor,
HomeForDesigner,
HomeColorPresets,
HomePricingPlans,
HomeAdvertisement,
HomeCleanInterfaces,
HomeHugePackElements,
} from '../sections/home';
// ----------------------------------------------------------------------
HomePage.getLayout = (page: React.ReactElement) => <MainLayout> {page} </MainLayout>;
// ----------------------------------------------------------------------
export default function HomePage() {
return (
<>
<Head>
<title> The starting point for your next project | Minimal UI</title>
</Head>
<ScrollProgress />
<HomeHero />
<Box
sx={{
overflow: 'hidden',
position: 'relative',
bgcolor: 'background.default',
}}
>
<HomeMinimal />
<HomeHugePackElements />
<HomeForDesigner />
<HomeDarkMode />
<HomeColorPresets />
<HomeCleanInterfaces />
<HomePricingPlans />
<HomeLookingFor />
<HomeAdvertisement />
</Box>
</>
);
}
What would the new layout.tsx, MuiSetup.tsx, theme.ts, and page.tsx (in the app folder) look like? Looking at the outstanding contributions by others in this thread, I have an idea and will give it a try today!! Thank you. :)
Here is an example to look at using the Next.js's /app
directory with tss-react
(which uses emotion
under the hood) until the PR is reviewed - https://github.com/mui/material-ui/pull/37315.
Here is an example to look at using the Next.js's
/app
directory withtss-react
(which usesemotion
under the hood) until the PR is reviewed - #37315.
So basically, does the whole application become a client side application then with 'use client'; in every single component??!
@alaindeurveilher Really weird. Seems absurd that to use the new RSC version of NextJS, we have to make our whole app client. Either the MUI doc is not understood or it is the NextJS one ; MUI seems compatible with a server side render: https://mui.com/material-ui/guides/server-rendering/
I have to prepare some POC for the teams of my company, I'll give it a try this weekend.
use client
on every component would defeat any benefit we'd get from using apps
:/
Guess we're not using Material-UI for new projects on NextJS 13 in the meantime.
@alaindeurveilher I have misunderstood; using "use client"
doesn't mean that everything is rendering on client side. NextJS always pre-render all our components on server side. With "use client"
it also sends the JS for the client for interactivity, lifecycle and hooks. It will remain a beautiful performing server app, and that's nice of it! π
@AlbinoGeek I thought the same thing at first. But in the "Good to know" part from the doc above, they say:
"use client" does not need to be defined in every file. The Client module boundary only needs to be defined once, at the "entry point", for all modules imported into it to be considered a Client Component.
So we could be good, I'll try it. π
I wanted to use MUI with Tailwind CSS. This is what I was able to do, though I am not sure if everything is implemented fine or not:
layout.tsx
import "./globals.css";
import Navigation from "./Navigation";
import ThemeWrapper from "./ThemeWrapper";
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
let menuLinks: { title: string; url: string }[] = [
{ title: "Home", url: "#" },
{ title: "Contact", url: "#" },
];
return (
<html lang="en">
<ThemeWrapper>
<body>
<Navigation menuLinks={menuLinks} />
{children}
</body>
</ThemeWrapper>
</html>
);
}
globals.css
@tailwind base;
@tailwind components;
@tailwind utilities;
ThemeWrapper.tsx
"use client";
import {
CssBaseline,
StyledEngineProvider,
ThemeProvider,
createTheme,
} from "@mui/material";
export default function ThemeWrapper({ children }: { children: any }) {
const theme = createTheme({});
return (
<StyledEngineProvider injectFirst>
<CssBaseline />
<ThemeProvider theme={theme}>{children}</ThemeProvider>
</StyledEngineProvider>
);
}
And then I'm seemingly able to use Tailwind css and mui components pretty easily. Here is an example component:
Hero.tsx
"use client"
...
export default function MainHero() {
const theme = useTheme();
return (
<div className="relative isolate overflow-hidden bg-gray-900">
<svg
className="absolute inset-0 -z-10 h-full w-full stroke-white/10 [mask-image:radial-gradient(100%_100%_at_top_right,white,transparent)]"
aria-hidden="true"
>
// more stuff
</div>
);
}
tailwind.config.js
/** @type {import('tailwindcss').Config} */
module.exports = {
corePlugins: {
preflight: false,
},
...
}
@Tigatok It seems to me that this approach forces all children components in the body to be client-sided, or am I overlooking something?
This PR with app router example looks close to merge https://github.com/mui/material-ui/pull/37315
This was fixed in https://github.com/mui/material-ui/pull/37315. Well done @smo043
I feel like, forcing every MUI component to render client side with "use client"
kind of defeats the purpose of an SSR-first framework like Next JS and Next 13. Since MUI is the UI framework, it will basically get used everywhere and make proper SSR useless or nearly impossible.
@Seanmclem I agree with what I interpret as the core of your argument (that MUI should make styling possible in server-only components aka React Server Components aka RSC), but it sounds like you're mixing things up - SSR is not the same as what Server Components provide. I think you may mean "Server Components-first", not "SSR-first" (since "use client"
components are also server-side rendered)
Hey guys, sorry I was not on track, is this issue still there with app router?
Hey guys, sorry I was not on track, is this issue still there with app router?
same problem still with app router. core problem is that className or Initial UIs are different on server side and client side. I using tailwindcss, and only condition ui and tailwindcss className with some props.
I really want to quit app router....
This might be helpful if you are using with tailwind css - https://ui.shadcn.com
Duplicates
Latest version
Summary π‘
There is a great example of how to use MUI with next.js (https://github.com/mui/material-ui/blob/master/examples/nextjs-with-typescript) in this repository.
Yet, a new version of Next.js was released recently: https://nextjs.org/blog/next-13
This introduces several new concepts and ways to write React apps with server-side components in mind. To migrate from the old Next.js to the new version, one needs to do several things:
app/
directory instead ofpages/
(although this can be done incrementally)app/
directory, one has to usefetch
(or any otherasync
/await
-based data fetching method instead ofgetInitialProps
,getServerSideProps
, orgetStaticProps
._document.tsx
and_app.tsx
should be refactored into a globallayout.tsx
: https://beta.nextjs.org/docs/upgrade-guide#migrating-_documentjs-and-_appjsIt would be great to get directions on how to use MUI with emotion styles and cache in this scenario. Would be even better to have a sample app like the one above.
Examples π
No response
Motivation π¦
Next.js 13 is a major new version that introduces several exciting features that would be really great to use in our apps. It would be great to have a guide or directions on how to integrate MUI with the new version of Next.js instead of the old
_document
and_app
based one.I think there will be many developers that would want to update to the newer version and might get stuck (like myself) on the MUI integration and how the emotion styles and cache work on this new "server-first" environment without
getInitialProps
.