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

Next.js 13 example with the app router #34898

Closed
2 tasks done
MartinXPN opened this issue Oct 26, 2022 · 44 comments
Closed
2 tasks done

Next.js 13 example with the app router #34898

MartinXPN opened this issue Oct 26, 2022 · 44 comments
Assignees
Labels
examples Relating to /examples

Comments

@MartinXPN
Copy link

MartinXPN commented Oct 26, 2022

Duplicates

  • I have searched the existing issues

Latest version

  • I have tested the 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:

  • Start using the app/ directory instead of pages/ (although this can be done incrementally)
  • Inside the components of the app/ directory, one has to use fetch (or any other async/await-based data fetching method instead of getInitialProps, getServerSideProps, or getStaticProps.
  • The _document.tsx and _app.tsx should be refactored into a global layout.tsx: https://beta.nextjs.org/docs/upgrade-guide#migrating-_documentjs-and-_appjs

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

@MartinXPN MartinXPN added the status: waiting for maintainer These issues haven't been looked at yet by a maintainer label Oct 26, 2022
@MartinXPN MartinXPN changed the title NextJS 13 example Next.js 13 example Oct 26, 2022
@Clumsy-Coder
Copy link

Also next/link no longer require <a> as a child of Link component

Check https://nextjs.org/blog/next-13#nextlink

Code example affected

// Add support for the sx prop for consistency with the other branches.
const Anchor = styled('a')({});
interface NextLinkComposedProps
extends Omit<React.AnchorHTMLAttributes<HTMLAnchorElement>, 'href'>,
Omit<NextLinkProps, 'href' | 'as' | 'onClick' | 'onMouseEnter' | 'onTouchStart'> {
to: NextLinkProps['href'];
linkAs?: NextLinkProps['as'];
}
export const NextLinkComposed = React.forwardRef<HTMLAnchorElement, NextLinkComposedProps>(
function NextLinkComposed(props, ref) {
const { to, linkAs, replace, scroll, shallow, prefetch, locale, ...other } = props;
return (
<NextLink
href={to}
prefetch={prefetch}
as={linkAs}
replace={replace}
scroll={scroll}
shallow={shallow}
passHref
locale={locale}
>
<Anchor ref={ref} {...other} />
</NextLink>
);
},
);

Temporary workaround

add prop legacyBehavior to next/link component

@MartinXPN
Copy link
Author

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.

@JanderSilv
Copy link

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: emotion-js/emotion#2928

@zannager zannager added the examples Relating to /examples label Oct 27, 2022
@MartinXPN
Copy link
Author

Is there any workaround that would enable us to use the app directory now without waiting for emotion and MUI library updates?

@mnajdova

This comment was marked as resolved.

@mnajdova mnajdova marked this as a duplicate of #34893 Oct 27, 2022
@mnajdova mnajdova added the duplicate This issue or pull request already exists label Oct 27, 2022
@github-actions github-actions bot removed the status: waiting for maintainer These issues haven't been looked at yet by a maintainer label Oct 27, 2022
@github-actions github-actions bot closed this as not planned Won't fix, can't repro, duplicate, stale Oct 27, 2022
@MartinXPN
Copy link
Author

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

@pomubry
Copy link

pomubry commented Oct 27, 2022

Temporary workaround

add prop legacyBehavior to next/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"];
}

@mnajdova mnajdova reopened this Oct 27, 2022
@dohomi
Copy link
Contributor

dohomi commented Oct 28, 2022

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 vercel/next.js#41994

@chris-askbrian
Copy link

Wouldnt it make sense to pin the versions of the used libs at least to the major versions?

@mnajdova
Copy link
Member

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

@PetroSilenius
Copy link
Contributor

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!

@gijsbotje
Copy link
Contributor

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

@PetroSilenius
Copy link
Contributor

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👍🏻

@gijsbotje
Copy link
Contributor

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

@akomm
Copy link
Contributor

akomm commented Nov 23, 2022

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.

@oliviertassinari oliviertassinari removed the duplicate This issue or pull request already exists label Nov 23, 2022
@AlbinoGeek
Copy link

AlbinoGeek commented Jan 7, 2023

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?

@danielcolgan
Copy link

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 use app/ directory for now?

Hi, did you find any resources for those workarounds?

Hey, did you find any solutions?

@rtrembecky
Copy link

rtrembecky commented Mar 17, 2023

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: emotion-js/emotion#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: vercel/next.js#41994 (comment))

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.

@Cielquan
Copy link

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;

@renanrms
Copy link

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.

@dougiefresh49
Copy link

dougiefresh49 commented Apr 20, 2023

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.

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

@arobert93
Copy link

arobert93 commented Apr 25, 2023

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

image

@dougiefresh49
Copy link

dougiefresh49 commented Apr 27, 2023

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


  1. in @Cielquan example code, he has this line with a comment
// 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 🎉

@Cielquan
Copy link

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

@Shivaansh-Agarwal
Copy link

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?

@coolwaveTC
Copy link

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

@ZeeshanTamboli
Copy link
Member

ZeeshanTamboli commented May 20, 2023

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

@alaindeurveilher
Copy link

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

So basically, does the whole application become a client side application then with 'use client'; in every single component??!

coulgreer added a commit to coulgreer/coulgreer.me that referenced this issue May 23, 2023
Setup the environment for testing and to leverage a component library
for expedience of building the site. It should, also, be noted that MUI
can only be used when a component is rendered client-side, instead of,
the prefered server-side. This is issue accures as of Next.js 13, and is
explained in further detail:
mui/material-ui#34898, mui/material-ui#37315
@caedes
Copy link

caedes commented May 25, 2023

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

@AlbinoGeek
Copy link

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.

@caedes
Copy link

caedes commented May 26, 2023

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

@Tigatok
Copy link

Tigatok commented May 30, 2023

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

@xn1cklas
Copy link

xn1cklas commented Jun 8, 2023

@Tigatok It seems to me that this approach forces all children components in the body to be client-sided, or am I overlooking something?

@MartinXPN MartinXPN changed the title Next.js 13 example with /app folder Next.js 13 example with the app router Jun 9, 2023
@AdamQuadmon
Copy link

This PR with app router example looks close to merge #37315

@mnajdova
Copy link
Member

This was fixed in #37315. Well done @smo043

@Seanmclem
Copy link

Seanmclem commented Jun 30, 2023

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.

@karlhorky
Copy link

karlhorky commented Jul 1, 2023

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

@Nikhilthadani
Copy link
Contributor

Hey guys, sorry I was not on track, is this issue still there with app router?

@smo043
Copy link
Contributor

smo043 commented Jul 4, 2023

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
examples Relating to /examples
Projects
None yet
Development

No branches or pull requests