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

Mantine with Next.js 13 app dir #2815

Closed
TamjidAhmed10 opened this issue Oct 27, 2022 · 139 comments
Closed

Mantine with Next.js 13 app dir #2815

TamjidAhmed10 opened this issue Oct 27, 2022 · 139 comments

Comments

@TamjidAhmed10
Copy link

What package has an issue

@mantine/core

Describe the bug

I use mantine dev as regular basis with next12. But when i put MantinProvider in app/pages.tsx or app/layout.tsx forlder. The app doesnt seems too work. Where to put __app.tsx requirements? sorry for bad english.

What version of @mantine/hooks page do you have in package.json?

"@mantine/hooks": "^5.6.2",

If possible, please include a link to a codesandbox with the reproduced problem

No response

Do you know how to fix the issue

No

Are you willing to participate in fixing this issue and create a pull request with the fix

No

Possible fix

Server Component support

@rtivital
Copy link
Member

app folder is not supported, you can use mantine-next-template in next 13 with pages directory the same way as with next 12.

@mikkurogue
Copy link

@rtivital Are you planning on supporting Next 13's app folder in the future?

@rtivital
Copy link
Member

Yes, it is planned, but it depends on emotion, there are several opened issues that you can track:

@rtivital
Copy link
Member

rtivital commented Oct 27, 2022

I got it working based on this issue (emotion-js/emotion#2928) – https://github.com/mantinedev/mantine-next-template/tree/next-13-app/app

@nwazuo
Copy link

nwazuo commented Nov 2, 2022

somehow, the above fix works for app/layout but using any mantine component in app/page throws the same error again. I didn't create my project from mantine-next-template. Creating the project with mantine-next-template makes everything work but I do not want to make a TypeScript project(just JavaScript).

@rtivital
Copy link
Member

rtivital commented Nov 2, 2022

All Mantine components can be used only if 'use client' is prepended to the file. As far as I know, pages are server only. You can use Mantine components only the way it is demonstrated in the template.

@imranbarbhuiya
Copy link
Contributor

imranbarbhuiya commented Nov 2, 2022

pages are server only

They default to server components but u can add 'use client' in page.tsx file to make it client component.

@DenisBessa
Copy link
Contributor

All Mantine components can be used only if 'use client' is prepended to the file. As far as I know, pages are server only. You can use Mantine components only the way it is demonstrated in the template.

Hi, thanks for the amazing project!

Are there any plans to make Mantine compatible with server components?

@rtivital
Copy link
Member

rtivital commented Nov 4, 2022

No, server components cannot have state/context/refs, it is not possible to build components without these things. Currently, it is not planned to add 'use client' to all components.

@johnhenry
Copy link

johnhenry commented Nov 4, 2022

Seems like this is technically a Next issue for which they have documentation (beta): https://beta.nextjs.org/docs/rendering/server-and-client-components#rendering-third-party-context-providers-in-server-components.

Whereas third-party maintainers can add the 'use client' directive, it doesn't seem reasonable to expect them to do so.

@anthonyalayo
Copy link

Seems like this is technically a Next issue

@johnhenry actually this is a react issue:
reactjs/rfcs#227

On the latest RFC:
https://github.com/reactjs/rfcs/blob/main/text/0188-server-components.md#basic-example

they decided on 'use client';. I personally would have preferred a 'use server'; but I'm sure there is still room for this to change during its beta.

@dexter4life
Copy link

I am having this problem

Unhandled Runtime Error
TypeError: Cannot read properties of null (reading 'useCallback')

When I upgrade to nextjs 13, any idea how I can fix this?

@vymao
Copy link

vymao commented Dec 29, 2022

Thanks for all of this. I'm a bit confused; I have been able to successfully use Mantine components in NextJS 13 without the use client directive?

@cjroth
Copy link

cjroth commented Jan 1, 2023

I got it working based on this issue (emotion-js/emotion#2928) – https://github.com/mantinedev/mantine-next-template/tree/next-13-app/app

This worked for me until I deployed to Vercel, then I got a blank white page. I managed to fix that using suggestions from emotion-js/emotion#2928 but got flickering issues where the CSS wasn't loaded until a moment after the DOM. Here is the modified version of @rtivital's code that solved everything for me:

'use client'

import { CacheProvider } from '@emotion/react'
import { ColorScheme, ColorSchemeProvider, createEmotionCache, MantineProvider } from '@mantine/core'
import { useLocalStorage } from '@mantine/hooks'
import { useServerInsertedHTML } from 'next/navigation'

// must be created outside of the component to persist across renders
const cache = createEmotionCache({ key: 'my' })
cache.compat = true

export default function RootStyleRegistry({ children }: { children: JSX.Element }) {
	useServerInsertedHTML(() => (
		<style
			data-emotion={`${cache.key} ${Object.keys(cache.inserted).join(' ')}`}
			dangerouslySetInnerHTML={{
				__html: Object.values(cache.inserted).join(' '),
			}}
		/>
	))

	return (
		<CacheProvider value={cache}>
				<MantineProvider
					withGlobalStyles
					withNormalizeCSS
					emotionCache={cache}
				>
					{children}
				</MantineProvider>
		</CacheProvider>
	)
}

@sumiren
Copy link

sumiren commented Jan 7, 2023

@rtivital
Are there any problems in putting this code into mantine/next?
#2815 (comment)

I also tried this code, and confirmed that it works on pre-rendering(SSR/SG) correctly.

If it doesn't bother you, I'll try to make PR.

@rtivital
Copy link
Member

rtivital commented Jan 7, 2023

It is not ready for production based on issue in emotion repo, I've implemented a POC. I'll update the template once app dir is out of beta and emotion provides documentation.

@activenode
Copy link

The version from @rtivital here from this repo seems to work fine for me but I'll see if I run into more problems.

@rtivital rtivital changed the title Where to implement MantineProvider in nextjs13? Mantine with Next.js 13 app dir Jan 22, 2023
@Depechie

This comment was marked as off-topic.

@rtivital
Copy link
Member

@Depechie Your issue is not related to app dir

@shawnmclean
Copy link

I tried the appDir template, works well until I tried to implement the theme changing behavior.

I ended up turning that off but if anyone found a way to drop the dark/light mode provider, please share.

@ericschmar
Copy link

ericschmar commented Feb 17, 2023

@cjroth

I got it working based on this issue (emotion-js/emotion#2928) – https://github.com/mantinedev/mantine-next-template/tree/next-13-app/app

This worked for me until I deployed to Vercel, then I got a blank white page. I managed to fix that using suggestions from emotion-js/emotion#2928 but got flickering issues where the CSS wasn't loaded until a moment after the DOM. Here is the modified version of @rtivital's code that solved everything for me:

'use client'

import { CacheProvider } from '@emotion/react'
import { ColorScheme, ColorSchemeProvider, createEmotionCache, MantineProvider } from '@mantine/core'
import { useLocalStorage } from '@mantine/hooks'
import { useServerInsertedHTML } from 'next/navigation'

// must be created outside of the component to persist across renders
const cache = createEmotionCache({ key: 'my' })
cache.compat = true

export default function RootStyleRegistry({ children }: { children: JSX.Element }) {
	useServerInsertedHTML(() => (
		<style
			data-emotion={`${cache.key} ${Object.keys(cache.inserted).join(' ')}`}
			dangerouslySetInnerHTML={{
				__html: Object.values(cache.inserted).join(' '),
			}}
		/>
	))

	return (
		<CacheProvider value={cache}>
				<MantineProvider
					withGlobalStyles
					withNormalizeCSS
					emotionCache={cache}
				>
					{children}
				</MantineProvider>
		</CacheProvider>
	)
}

I've got this running in my own repo, but I'm still experiencing dramatic layout shifts on the first load of the website. From any page, doing cmd + r will cause pretty bad layout shifts. Does this happen to you?

Before, Mantine docs had a getInitialProps() that was used to modify the document context and supply a styles server that was linked up with emotion. I'm a 0% expert on either of these things, so I'm moreso wondering if this solution should also do the same thing? The previous method I mentioned got rid of layout shifts between refreshes. But now it is back.

@zenyr
Copy link

zenyr commented Feb 18, 2023

After searching through numerous cases I'd like to share my "current" workaround for Next.js 13 + Mantine + Emotion setup. It seems like working without FOUC/layout shifting for my minimal POC but this might break in certain cases. Please do share your experiences.

Also I've extracted out critical logic into a single hook. You can merge the extracted hook into the 'emotion.tsx' if that's your jam.

// 1. /app/layout.tsx
// use relative imports if you have to
import EmotionProvider from '$/components/Providers/emotion';

export default function RootLayout({ children }: { children: JSX.Element }) {
  return (
    <html>
      <head></head>
      <body>
        <EmotionProvider>
          {children}
        </EmotionProvider>
      </body>
    </html>
  );
}

// since layout.tsx & page.tsx is a Server Component you can do...
// const metadata : MetaData = { title: 'Awesome Mantine' }; jazz as you wish


// 2. /components/Providers/emotion.tsx
'use client';
import { useGluedEmotionCache } from '$/lib/emotionNextjsGlue';
import { CacheProvider } from '@emotion/react';
import { MantineProvider } from '@mantine/core';

export default function EmotionProvider({ children }: { children: JSX.Element }) {
  const cache = useGluedEmotionCache();
  return (
    <CacheProvider value={cache}>
    {/* You can wrap ColorSchemeProvider right here but skipping that for brevity ;) */} 
      <MantineProvider withGlobalStyles withNormalizeCSS emotionCache={cache}>
        {children}
      </MantineProvider>
    </CacheProvider>
  );
}

// 3. /lib/emotionNextjsGlue.tsx
import createCache from '@emotion/cache';
import { useServerInsertedHTML } from 'next/navigation';
import { useState } from 'react';

export const useGluedEmotionCache = (key = 'emotion') => {
  const [cache] = useState(() => {
    const cache = createCache({ key });
    cache.compat = true;
    return cache;
  });

  useServerInsertedHTML(() => {
    const entries = Object.entries(cache.inserted);
    if (entries.length === 0) return null;
    const names = entries
      .map(([n]) => n)
      .filter((n) => typeof n === 'string')
      .join(' ');
    const styles = entries.map(([, s]) => s).join('\n');
    const emotionKey = `${key} ${names}`;
    return <style data-emotion={emotionKey} dangerouslySetInnerHTML={{ __html: styles }} />;
  });

  return cache;
};

I don't use css={...} prop at the moment but if you set up jsx import source properly to @emotion/react, css prop works as expected too.

Edit: simplified hook a bit

@Tansi-Jones
Copy link

Tansi-Jones commented Feb 19, 2023

Hello, please when will mantine ui support next app dir? I've been trying to get it to work to no avail, help?

@imranbarbhuiya
Copy link
Contributor

imranbarbhuiya commented Jun 21, 2023

What about panda-css.com

U can use it with mantine 7 instead of css module

@khantwaikyaw
Copy link

Thanks. I'll try it.

@s2skyinc
Copy link

  • error node_modules@mantine\core\esm\core\MantineProvider\MantineThemeProvider\MantineThemeProvider.js (10:0) @ useMantineTheme
  • error Error: @mantine/core: MantineThemeProvider is not found in component tree, make sure you have it in your app

@Nishchit14
Copy link

Will I be able to use mantine v7 as a server component?

I am wondering what should be the cause as Nextjs is allowing the workaround for it. Is it breaking anything?
https://nextjs.org/docs/getting-started/react-essentials#rendering-third-party-context-providers-in-server-components

@khantwaikyaw
Copy link

Will I be able to use mantine v7 as a server component?

I am wondering what should be the cause as Nextjs is allowing the workaround for it. Is it breaking anything?
https://nextjs.org/docs/getting-started/react-essentials#rendering-third-party-context-providers-in-server-components

Sure. The components can be used in SSR without ```
use client

 

@YechiamW3
Copy link

So I wasn't sure if this should be in this discussion or in a different issue, but it's more of a question.
In Next 13 there are layouts, which if I understand correctly are similar in practice to AppShell (except being SSR).
My question is, is it possible to use AppShell in Next 13's layouts (mainly the RootLayout)?
If not, should we rely on AppShell or N13's layout system to design layouts? I'm inclined to use N13's layouts as they provide composite consistency between pages, but I'd rather have AppShell's design system.

@ItaiAxelrad
Copy link
Contributor

My question is, is it possible to use AppShell in Next 13's layouts (mainly the RootLayout)?

@YechiamTK Probably should be a discussion but, yes, you can. Create a component with the AppShell (with the 'use client' directive) that accepts the { children } prop and wrap your RootLayout with it:

// MainAppShell.tsx
'use client'
// ...
export default function MainAppShell({
  children,
}: {
  children: React.ReactNode;
}) {
  const [opened, setOpened] = useState(false);

  return (
    <AppShell
      header={<AppHeader />}
      navbar={<AppNavbar opened={opened} setOpened={setOpened} />}
      footer={<AppFooter />}
    >
      {children}
    </AppShell>
  );
}
// layout.tsx
// ...
export default function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
<html lang='en-US' dir='ltr'>
  <body className={`${inter.className}`}>
   <MainAppShell>{children}</MainAppShell>
  </body>
</html>
  );
}

@ctretyak
Copy link

What's the difference between Mantine and chakra-ui? I use the same emotion cache, but emotion doesn't collect mantine's components styles (only global) when the Chakra's does.

image
image

@masterbater
Copy link

I got it working based on this issue (emotion-js/emotion#2928) – https://github.com/mantinedev/mantine-next-template/tree/next-13-app/app

Is it possible to update this using mui example on injection. With this example some styles are duplicated
https://mui.com/material-ui/guides/next-js-app-router/

@ItaiAxelrad
Copy link
Contributor

@masterbater here is my basic app router set-up (with server inserted cache):

export const myCache = createEmotionCache({ key: 'mantine' });
//...
const cache = useEmotionCache();
cache.compat = true;
//...
  useServerInsertedHTML(() => (
    <style
      data-emotion={`${cache.key} ${Object.keys(cache.inserted).join(' ')}`}
      dangerouslySetInnerHTML={{
        __html: Object.values(cache.inserted).join(' '),
      }}
    />
  ));

return (
<CacheProvider value={cache}>
  <MantineProvider emotionCache={myCache} withGlobalStyles>
     {children}
  </MantineProvider>
</CacheProvider>
)

@masterbater
Copy link

@masterbater here is my basic app router set-up (with server inserted cache):

export const myCache = createEmotionCache({ key: 'mantine' });
//...
const cache = useEmotionCache();
cache.compat = true;
//...
  useServerInsertedHTML(() => (
    <style
      data-emotion={`${cache.key} ${Object.keys(cache.inserted).join(' ')}`}
      dangerouslySetInnerHTML={{
        __html: Object.values(cache.inserted).join(' '),
      }}
    />
  ));

return (
<CacheProvider value={cache}>
  <MantineProvider emotionCache={myCache} withGlobalStyles>
     {children}
  </MantineProvider>
</CacheProvider>
)

I have that bro, issue is some styles I setup in component got overriden by the default styling of mantine. There might be some order issue with the existing example provided. So I use mui style and it fixes all the issue

@ItaiAxelrad
Copy link
Contributor

@masterbater ah, I see. Thought maybe you were missing the mantine cache key

@masterbater
Copy link

Reusing mantine cache or creating new one same issue. but anyway i have already complete integration from rtl to switch theme without any issue now, no FOUC

@CoolCoder555
Copy link

Pease check the next 13 doc
Click here

@wachidmudi
Copy link

wachidmudi commented Aug 7, 2023

Am i just missing or has anybody notice that the css classes inserted twice, either in development or production? Just stumble upon the issue from here. Just add some extra checking to reduce the page size. cmiiw.

'use client';

import { CacheProvider } from '@emotion/react';
import { createEmotionCache, MantineProvider } from '@mantine/core';
import { useServerInsertedHTML } from 'next/navigation';
import { useRef } from 'react';

// must be created outside of the component to persist across renders
const cache = createEmotionCache({ key: 'my' });
cache.compat = true;

export default function RootStyleRegistry({
  children,
}: {
  children: JSX.Element;
}) {
  const isServerInserted = useRef(false);

  useServerInsertedHTML(() => {
    if (!isServerInserted.current) {
      isServerInserted.current = true;
      return (
        <style
          data-emotion={`${cache.key} ${Object.keys(cache.inserted).join(' ')}`}
          dangerouslySetInnerHTML={{
            __html: Object.values(cache.inserted).join(' '),
          }}
        />
      );
    }
  });

  return (
    <CacheProvider value={cache}>
      <MantineProvider withGlobalStyles withNormalizeCSS emotionCache={cache}>
        {children}
      </MantineProvider>
    </CacheProvider>
  );
}

@Jared-Dahlke
Copy link

Jared-Dahlke commented Aug 16, 2023

I got it working based on this issue (emotion-js/emotion#2928) – https://github.com/mantinedev/mantine-next-template/tree/next-13-app/app

This worked for me until I deployed to Vercel, then I got a blank white page. I managed to fix that using suggestions from emotion-js/emotion#2928 but got flickering issues where the CSS wasn't loaded until a moment after the DOM. Here is the modified version of @rtivital's code that solved everything for me:

'use client'

import { CacheProvider } from '@emotion/react'
import { ColorScheme, ColorSchemeProvider, createEmotionCache, MantineProvider } from '@mantine/core'
import { useLocalStorage } from '@mantine/hooks'
import { useServerInsertedHTML } from 'next/navigation'

// must be created outside of the component to persist across renders
const cache = createEmotionCache({ key: 'my' })
cache.compat = true

export default function RootStyleRegistry({ children }: { children: JSX.Element }) {
	useServerInsertedHTML(() => (
		<style
			data-emotion={`${cache.key} ${Object.keys(cache.inserted).join(' ')}`}
			dangerouslySetInnerHTML={{
				__html: Object.values(cache.inserted).join(' '),
			}}
		/>
	))

	return (
		<CacheProvider value={cache}>
				<MantineProvider
					withGlobalStyles
					withNormalizeCSS
					emotionCache={cache}
				>
					{children}
				</MantineProvider>
		</CacheProvider>
	)
}

This works great, but does anyone have an example of how to do this implementing toggleColorScheme or some way to let user toggle the color scheme?

Before App dir I used <ColorSchemeProvider> for this.

The workarounds so far are great, but none of them mention how to handle a changeable color scheme that I can find.

@CodeMode365
Copy link

Maybe we can do this in the way that the Redux Provider is used in the separate file as client component and used in the root layout

@ManInTheWind
Copy link

ManInTheWind commented Aug 18, 2023

I got it working based on this issue (emotion-js/emotion#2928) – https://github.com/mantinedev/mantine-next-template/tree/next-13-app/app

This worked for me until I deployed to Vercel, then I got a blank white page. I managed to fix that using suggestions from emotion-js/emotion#2928 but got flickering issues where the CSS wasn't loaded until a moment after the DOM. Here is the modified version of @rtivital's code that solved everything for me:

'use client'

import { CacheProvider } from '@emotion/react'
import { ColorScheme, ColorSchemeProvider, createEmotionCache, MantineProvider } from '@mantine/core'
import { useLocalStorage } from '@mantine/hooks'
import { useServerInsertedHTML } from 'next/navigation'

// must be created outside of the component to persist across renders
const cache = createEmotionCache({ key: 'my' })
cache.compat = true

export default function RootStyleRegistry({ children }: { children: JSX.Element }) {
	useServerInsertedHTML(() => (
		<style
			data-emotion={`${cache.key} ${Object.keys(cache.inserted).join(' ')}`}
			dangerouslySetInnerHTML={{
				__html: Object.values(cache.inserted).join(' '),
			}}
		/>
	))

	return (
		<CacheProvider value={cache}>
				<MantineProvider
					withGlobalStyles
					withNormalizeCSS
					emotionCache={cache}
				>
					{children}
				</MantineProvider>
		</CacheProvider>
	)
}

This works great, but does anyone have an example of how to do this implementing toggleColorScheme or some way to let user toggle the color scheme?

Before App dir I used <ColorSchemeProvider> for this.

The workarounds so far are great, but none of them mention how to handle a changeable color scheme that I can find.

try this, it work for me @Jared-Dahlke

export default function RootStyleRegistry({
  children,
}: {
  children: React.ReactNode;
}) {
  const cache = useEmotionCache();
  cache.compat = true;

  const [colorScheme, setColorScheme] = useLocalStorage<ColorScheme>({
    key: "mantine-color-scheme",
    defaultValue: "light",
    getInitialValueInEffect: true,
  });
  useHotkeys([["mod+J", () => toggleColorScheme()]]);

  const toggleColorScheme = (value?: ColorScheme) => {
    // console.log("RootStyleRegistry value", colorScheme);
    setColorScheme(value || (colorScheme === "dark" ? "light" : "dark"));
    document.body.style.background =
        colorScheme === "dark"
            ? "var(--mantine-color-white)"
            : "var(--mantine-color-dark-7)";
    document.body.style.color =
        colorScheme === "dark"
            ? "var(--mantine-color-black)"
            : "var(--mantine-color-dark-0)";
  };

  useServerInsertedHTML(() => (
    <style
      data-emotion={`${cache.key} ${Object.keys(cache.inserted).join(" ")}`}
      dangerouslySetInnerHTML={{
        __html: Object.values(cache.inserted).join(" "),
      }}
    />
  ));

  return (
    <CacheProvider value={cache}>
      <ColorSchemeProvider
        colorScheme={colorScheme}
        toggleColorScheme={toggleColorScheme}
      >
        <MantineProvider
          theme={{ colorScheme }}
          withGlobalStyles
          withNormalizeCSS
          withCSSVariables
        >
          {children}
        </MantineProvider>
      </ColorSchemeProvider>
    </CacheProvider>
  );
}

@nilsgarland
Copy link

Can we presume this as fixed now in 7.0? @rtivital
3 months since Next 13 stable release...

@rexxDigital
Copy link

Can we presume this as fixed now in 7.0? @rtivital 3 months since Next 13 stable release...

We pray

@DenisBessa
Copy link
Contributor

I can confirm this is fixed in Mantine v7!

@rtivital
Copy link
Member

The issue is resolved in 7.0. Now you can use Mantine with Next.js app router without any additional setup. You can find templates and additional information on the Next.js guide page – https://mantine.dev/guides/next

Thanks everyone for the feedback on this issue!

@rtivital rtivital unpinned this issue Sep 24, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests