How to use mantine UI with next 13 - reactjs

I was trying to use mantine UI with NextJS 13 using the app directory,
I have to wrap everything in the MantineProvider component but I don't know where to put it.
I tried this
layout.js
/* eslint-disable #next/next/no-head-element */
import { MantineProvider } from '#mantine/core';
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<MantineProvider
withGlobalStyles
withNormalizeCSS
theme={{
/** Put your mantine theme override here */
colorScheme: 'dark',
}}>
<html>
<head></head>
<body>{children}</body>
</html>
</MantineProvider>
);
}
and it didn't work
so, is there any solution??

So I've been interested in solving this problem too.
Step 1 is moving third-party providers to a "client-only" component.
See here
The next step is to follow this thread on mantine's github, while they work out compatibility issues with emotion & next13
Lastly, this seems to be the only official implementation example on Mantine's github using Mantine with the new Next.js app directory.
Here's how they approached it:
/app/emotion.tsx
"use client";
import { CacheProvider } from "#emotion/react";
import { useEmotionCache, MantineProvider } from "#mantine/core";
import { useServerInsertedHTML } from "next/navigation";
export default function RootStyleRegistry({
children
}: {
children: React.ReactNode
}) {
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 withGlobalStyles withNormalizeCSS>
{children}
</MantineProvider>
</CacheProvider>
)
}
/app/layout.tsx
import RootStyleRegistry from './emotion';
export default function RootLayout({ children }) {
return (
<html lang="en-US">
<head />
<body>
<RootStyleRegistry>{children}</RootStyleRegistry>
</body>
</html>
);
}
Hope this helps. Let me know if you get it working

Related

chakra ui custom theme doesn't work at all in my next js project

I'm trying to build nextjs project with chakra UI, but my custom theme doesn't working at all, I've tried everything in chakra docs, even custom color which I've added doesn't work, but default colors of the chakra works correctly, I wanna info that I've setup dark/light mode using chakra docs, if it has something to to with my problem
My theme.js file
import { extendTheme } from "#chakra-ui/react";
import { mode } from "#chakra-ui/theme-tools";
const styles = {
global: (props) => ({
body: {
bg: mode("#a8dadc", "#006d77")(props),
color: mode("#006d77", "#a8dadc")(props),
},
}),
};
const colors = {
primary: "#e29578",
};
const theme = extendTheme({ styles, colors });
export default theme;
My index.tsx file
import {
Box,
Button,
useColorMode,
Text,
} from "#chakra-ui/react";
import type { NextPage } from "next";
import Head from "next/head";
const Home: NextPage = () => {
const { colorMode, toggleColorMode } = useColorMode();
return (
<div>
<Head>
<title>Create Next App</title>
<meta name="description" content="Generated by create next app" />
<link rel="icon" href="/favicon.ico" />
</Head>
<h1>Home Page</h1>
<Box mb={4}>This boxs style will change based on the color mode.</Box>
<Button onClick={toggleColorMode} color="primary">
Toggle {colorMode === "light" ? "Dark" : "Light"}
</Button>
<Text fontSize="6xl" color="primary">
{" "}
Custom color not working
</Text>
</div>
);
};
export default Home;
My _app.tsx file
import type { AppProps } from "next/app";
import { ChakraProvider } from "#chakra-ui/react";
import { Chakra } from "../components/wrappers/Chakra";
import theme from "../libs/theme";
function MyApp({ Component, pageProps }: AppProps) {
return (
<ChakraProvider theme={theme}>
<Chakra cookies={pageProps.cookies}>
<Component {...pageProps} />
</Chakra>
</ChakraProvider>
);
}
export { getServerSideProps } from "../components/wrappers/Chakra";
export default MyApp;
I have the same issue!
I've noticed that there are some default css variables attached to :root an the ChakraProvider is not able to override them by default, so I've set the cssVarsRoot as a workarround.
<ChakraProvider theme={theme} cssVarsRoot="body">

React Intl Could not find required `intl` object. <IntlProvider> needs to exist in the component ancestry

I have used latest version of React-intl(^5.20.2). Trying to achieve Enzyme Unit testing in React hook component. but throwing this error "[React Intl] Could not find required intl object. needs to exist in the component ancestry." on UseIntl() inside functional component while running tests
{intl.formatMessage({ id: "Welcome" })}
You need to wrap the component under test with the IntlProvider component. You can do this by creating a wrapper to pass in the options with the render utility.
Wrapper
Pass a React Component as the wrapper option to have it rendered
around the inner element. This is most useful for creating reusable
custom render functions for common data providers.
Example:
import { IntlProvider } from 'react-intl';
export const IntlWrapper = ({ children }) => (
<IntlProvider locale="en">{children}</IntlProvider>
);
Test code
import { render } from '#testing-library/react';
import { IntlWrapper } from '../path/to/testUtils';
...
render(
<ComponentUnderTest />,
{
wrapper: IntlWrapper,
},
);
Okay so in my case it was due to SSR.
In addition of the "locale" Provider which allow language switching.
I've added to my _document.jsx a Provider which get the locale from the router.
class MyDocument extends Document<TDocumentInitialProps> {
static async getInitialProps(ctx: DocumentContext): Promise<TDocumentInitialProps> {
const messages = (await importMessages(ctx.locale)) as Record<string, string> | Record<string, MessageFormatElement[]>;
return {
locale: ctx.locale,
messages,
};
}
render(): JSX.Element {
const { locale, messages } = this.props;
return (
<IntlProvider locale={locale} messages={messages}>
<Html lang={locale}>
<Head>
<link rel="icon" href="/images/layout/favicon.png" />
</Head>
<body>
<Main />
<NextScript />
</body>
</Html>
</IntlProvider>
);
}
}
export default MyDocument;

Next.js 9+ FOUC (Flash or Unstyled Content) with Styled Components

Have spent a couple days on this issue sourcing solutions and ideas from most of SA and Reddit however to no avail..
Upon load both in production and local every load presents the whole html without any styling before then being injected into DOM..
Currently this is my projects key files:
_document.js
import { ServerStyleSheet } from "styled-components";
import Document, { Main, NextScript } from "next/document";
export default class MyDocument extends Document {
static async getInitialProps(ctx) {
const sheet = new ServerStyleSheet();
const originalRenderPage = ctx.renderPage;
try {
ctx.renderPage = () =>
originalRenderPage({
enhanceApp: (App) => (props) =>
sheet.collectStyles(<App {...props} />),
});
const initialProps = await Document.getInitialProps(ctx);
return {
...initialProps,
styles: (
<>
{initialProps.styles}
{sheet.getStyleElement()}
</>
),
};
} finally {
sheet.seal();
}
}
render() {
return (
<html lang="en">
<Head>
<title>namesjames</title>
<link rel="icon" href="/favicon.ico" />
<script src="/static/chrome-fix.js" />
<link href="/above-the-fold.css" />
</Head>
<body>
<script src="/noflash.js" />
<Main />
<NextScript />
<script> </script>
</body>
</html>
);
}
}
_app.js
/* eslint-disable class-methods-use-this */
import App from "next/app";
import React from "react";
import { ThemeProvider } from "styled-components";
import { ParallaxProvider } from 'react-scroll-parallax';
import Header from "../components/Header";
import theme from "../theme";
import GlobalStyles from "../GlobalStyles";
import DarkModeToggle from '../components/toggle/toggleMode';
import Footer from '../components/Footer'
import LazyLoad from 'react-lazy-load';
import Router from 'next/router';
import styled from 'styled-components'
// import '../style.css'
const Loaded = styled.div`
opacity: ${(props) => props.loaded ? "1" : "0"};
`
export default class MyApp extends App {
state = { isLoading: false, loaded: false }
componentDidMount() {
// Logging to prove _app.js only mounts once,
// but initializing router events here will also accomplishes
// goal of setting state on route change
console.log('MOUNT');
this.setState({loaded: true})
Router.events.on('routeChangeStart', () => {
this.setState({ isLoading: true });
console.log('loading is true, routechangeStart')
});
Router.events.on('routeChangeComplete', () => {
this.setState({ isLoading: false });
console.log('loading is false, routeChangeComplete')
});
Router.events.on('routeChangeError', () => {
this.setState({ isLoading: false });
console.log('loading is false, routeChangeError')
});
}
render(): JSX.Element {
const { isLoading } = this.state;
const { Component, pageProps, router, loaded } = this.props;
return (
<Loaded loaded={this.state.loaded}>
<ThemeProvider theme={theme}>
<GlobalStyles />
<ParallaxProvider>
<Header />
{isLoading && 'STRING OR LOADING COMPONENT HERE...'}
<Component {...pageProps} key={router.route} />
<LazyLoad offsetVertical={500}>
<Footer />
</LazyLoad>
</ParallaxProvider>
<DarkModeToggle />
</ThemeProvider>
</Loaded>
);
}
}
index.js
import { color } from "styled-system";
import { OffWhite } from "../util/tokens";
import Hero from "../components/Hero";
import Banner from '../components/Banner'
import TitleText from '../components/TitleText'
import HomeTitleCopyScene from '../components/HomeTitleCopyScene'
import TwoCards from '../components/TwoCards'
import LazyLoad from 'react-lazy-load';
function Home(): JSX.Element {
return (
<div className="container">
<Banner />
<HomeTitleCopyScene />
<LazyLoad offsetVertical={1000}>
<TwoCards />
</LazyLoad>
</div>
);
}
export default Home;
As some might see I have tried multiple implementations and am just a bit confused as to what it could be at this stage..
Any help appreciated and I can provide more information upon request.. Many thanks
I found two solutions which helped -->
Was to hard style opacity:0 into the JSX and then upon styling injecting into DOM applying opacity: 1 !important onto any of the components displayed..
<section className="cards-block" style={{opacity:0}}>
Whilst this was effective this morning I discovered at some point during my development I had incorrectly imported Head from next/head and used this in my _document.js rather than using the correct Head from next/documents..
// import Head from "next/head"; --> incorrect
import { ServerStyleSheet } from "styled-components";
import Document, { Head, Main, NextScript } from "next/document";
Ergo -> a correctly rendering and injected element with no FOUC
Hope this helps someone out there
I've found a workaround for my small portfolio project:
just included this inline css to a <head> of my custom _document.js:
{<style dangerouslySetInnerHTML={{__html: `
html {background: #333}
body #__next div {visibility: hidden}
body.loaded #__next div {visibility: visible}
`}}></style>}
The "loaded" class is added to the body in _app.js:
if (process.browser) {
document.body.classList.add("loaded")
}
I'm hot sure if it's a good solution, any advice would be appreciated :)

Only load Snipcart on specific page in Gatsby

I'm using Snipcart Plugin in Gatsby but the script gets loaded everywhere. Is is it possible with some sort of function to trigger the script on only 1 specific page and not entirely?
Below are the options I'm using in my Gatsby-config.js file
{
resolve: "gatsby-plugin-snipcart",
options: {
apiKey: process.env.SNIPCART_API,
autopop: true,
js: "https://cdn.snipcart.com/themes/v3.0.8/default/snipcart.js",
styles: "https://cdn.snipcart.com/themes/v3.0.8/default/snipcart.css",
jquery: false,
},
},
You should take a look at gatsby-plugin-snipcartv3. I believe the gatsby-plugin-snipcart is deprecated and is not working with Snipcart v3.
But as far as I know, there's no way to tell the plugin to load the script only on specific pages.
You could use Snipcart directly, not using the plugin, to have more control over it.
Let's say you have a layout.js file, wrapping content for your page, you can have a loadSnipcart flag that will load Snipcart files only when you need them.
Here's an example:
layout.js
import React from "react"
import Helmet from "react-helmet"
export default ({ loadSnipcart, children }) => {
const Snipcart = () => {
if (!loadSnipcart) return null
return (
<Helmet>
<script
src="https://cdn.snipcart.com/themes/v3.0.8/default/snipcart.js"
type="text/javascript"
/>
<link
href="https://cdn.snipcart.com/themes/v3.0.8/default/snipcart.css"
rel="stylesheet"
/>
</Helmet>
)
}
return (
<div id="main-content">
<Snipcart />
{children}
</div>
)
}
shop.js
import React from "react"
import Layout from "./layout"
export default () => {
return (
<Layout loadSnipcart>
<h1>Welcome to my shop !</h1>
</Layout>
)
}
index.js
import React from "react"
import Layout from "./layout"
export default () => {
return (
<Layout>
<h1>This page doesn't load Snipcart</h1>
</Layout>
)
}

Intense FOUC when using Next.js 8 and styled-jsx

I have recently upgraded to Next.js 8.0.3 from 6.1.1 and I am now encountering a very intense flash of un-styled content (FOUC) for my header content which is using styled-jsx. It loaded just fine before updating Next.js.
The header code that is flashing is a custom built npm module that uses styled-jsx (but not next) and is being imported and placed into a layout page that is rendered with every next page.
This was the implementation in the _document.js file before updating next and it was working:
import Document, { Head, Main, NextScript } from 'next/document'
import { ServerStyleSheet, injectGlobal } from 'styled-components'
import styledNormalize from 'styled-normalize'
import flush from 'styled-jsx/server'
injectGlobal`
${styledNormalize}
`
export default class MyDocument extends Document {
static getInitialProps({ renderPage }) {
const sheet = new ServerStyleSheet()
const page = renderPage(App => props =>
sheet.collectStyles(<App {...props} />)
)
const styleTags = sheet.getStyleElement()
const styles = flush()
return { ...page, styleTags, styles }
}
render() {
return (
<html>
<Head>
{this.props.styleTags}
</Head>
<body>
<Main />
<NextScript />
</body>
</html>
)
}
}
Based on the docs I have also tried this (where I wait for the initial props):
import Document, { Head, Main, NextScript } from 'next/document'
import { ServerStyleSheet, injectGlobal } from 'styled-components'
import styledNormalize from 'styled-normalize'
import flush from 'styled-jsx/server'
injectGlobal`
${styledNormalize}
`
export default class MyDocument extends Document {
static async getInitialProps(ctx) {
const sheet = new ServerStyleSheet()
const page = ctx.renderPage(App => props =>
sheet.collectStyles(<App {...props} />)
)
const initialProps = await Document.getInitialProps(ctx)
const styleTags = sheet.getStyleElement()
const styles = flush()
return { ...initialProps, ...page, styles, styleTags }
}
render() {
return (
<html>
<Head>
{this.props.styleTags}
</Head>
<body>
<Main />
<NextScript />
</body>
</html>
)
}
}
The flash might be a result of where I am implementing the module but not sure.
It seems like the code coming in from the module is not being properly bundled with the rest of the pages and thus giving the page flash. Any thoughts or feedback would be appreciated.
I ended up fixing the issue by refactoring the custom npm module to not use styled-jsx but instead use styled-components. Not really a fix but more of a work around

Resources