I'm building a react app using material ui and nextjs. I'm using <Autocomplete /> component, provided by material UI and override some its styles with my own like this:
<Autocomplete
classes={{
root: `${styles[`search__autocomplete`]} ${
styles[`search--${variant}__autocomplete`]
}`,
inputRoot: `${styles[`search__autocomplete-input`]} ${
styles[`search--${variant}__autocomplete-input`]
}`
}}
/>
variant is a prop, which gets passed to the component and styles is a variable, which get imported from the css module: import styles from "Search.module.sass".
Now, when I'm working on this locally everything looks great:
But, after I deploy it to production via next build && next export I start experiencing "flickering" effect when for like 1/3 of a second my page looks like this:
My guess is that it might be related to the fact that nextjs export my css to several files on production:
<link
rel="preload"
href="/_next/static/css/4dcd7fa805fb41261f08.css"
as="style"
/>
<link
rel="stylesheet"
href="/_next/static/css/4dcd7fa805fb41261f08.css"
data-n-g=""
/>
<link
rel="preload"
href="/_next/static/css/a23cf79bceae4047fddb.css"
as="style"
/>
<link
rel="stylesheet"
href="/_next/static/css/a23cf79bceae4047fddb.css"
data-n-p=""
/>
How can I solve that?
The solution was to create a custom pages/_document.js page with:
import React from 'react';
// Modules
import Document, { Html, Head, Main, NextScript } from 'next/document';
// MUI Core
import { ServerStyleSheets } from '#material-ui/core/styles';
class MyDocument extends Document {
render() {
return (
<Html lang="en">
<Head>
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Open+Sans:wght#300;400;600;700&display=swap" />
</Head>
<body>
<Main />
<NextScript />
</body>
</Html>
);
}
}
// `getInitialProps` belongs to `_document` (instead of `_app`),
// it's compatible with server-side rendering (SSR).
MyDocument.getInitialProps = async (ctx) => {
// Resolution order
//
// On the server:
// 1. app.getInitialProps
// 2. page.getInitialProps
// 3. document.getInitialProps
// 4. app.render
// 5. page.render
// 6. document.render
//
// On the server with error:
// 1. document.getInitialProps
// 2. app.render
// 3. page.render
// 4. document.render
//
// On the client
// 1. app.getInitialProps
// 2. page.getInitialProps
// 3. app.render
// 4. page.render
// Render app and page and get the context of the page with collected side effects.
const sheets = new ServerStyleSheets();
const originalRenderPage = ctx.renderPage;
ctx.renderPage = () => originalRenderPage({
enhanceApp: (App) => (props) => sheets.collect(<App {...props} />),
});
const initialProps = await Document.getInitialProps(ctx);
return {
...initialProps,
// Styles fragment is rendered after the app and page rendering finish.
styles: [
...React.Children.toArray(initialProps.styles),
sheets.getStyleElement(),
],
};
};
export default MyDocument;
In my case it was caused by the <CacheProvider> component of #emotion/react removing it from _app.js file fixed the problem for me
Nextjs is now supporting SSR with emotion you can enable it this way in next.config.js file :
const nextConfig = {
//...
compiler: {
emotion: true,
},
};
Also if using <CssBaseline/> component of Material UI replace it with <ScopedCssBaseline>
However, you might be progressively migrating a website to MUI, using a global reset might not be an option. It's possible to apply the baseline only to the children by using the ScopedCssBaseline component.
import { ScopedCssBaseline } from "#mui/material";
//...
<ScopedCssBaseline>
<Component {...pageProps} />
</ScopedCssBaseline>
//...
Related
I have some problem with icons and style were used in NextJs. I use material-ui in my project, developed time everything is ok but when i build project, the icons first be large then it gets normal. I try used other icon sets but result isn't change. Is it a NextJs bug?.
Before :
After :
This seem to help me with MUI icons
Before:
<WebAssetOffIcon sx={{maxWidth: 60, maxHeight: 60 }} />
After:
<WebAssetOffIcon style={{ maxWidth: 60, maxHeight: 60 }} />
Somone mentioned:
"The cause is the fact that the icon is being rendered before the CSS is loaded."
You can use the width property to set the icon default size, so when the CSS completely loads, they change to the icon's size.
<Icon width="16" />
add below code to your app.js/ts file it should fix your problem
import { config } from '#fortawesome/fontawesome-svg-core' // 👈
import '#fortawesome/fontawesome-svg-core/styles.css' // 👈
config.autoAddCss = false // 👈
credit goes https://github.com/FortAwesome/react-fontawesome/issues/234
Please add this code to "_document.js"
_document.js
import Document, {Html, Head, NextScript, Main} from 'next/document'
import {ServerStyleSheet} from 'styled-components';
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();
return {...page, styleTags};
}
render() {
return (
<Html lang="en">
<Head/>
<body>
<Main />
<NextScript/>
</body>
</Html>
)
}
}
~ Storm In Talent
I'm getting two errors when I try to yarn run build my nextJS project:
3:1 Error: next/document should not be imported outside of pages/_document.js.
See https://nextjs.org/docs/messages/no-document-import-in-page. #next/next/no-document-import-in-page
13:38 Error: Component definition is missing display name react/display-name
the first error doesn't make sense to me, see the path of my _document.js:
the second, doesn'nt uderstand, my _document.js:
import Document, { Head, Main, NextScript } from "next/document";
// Import styled components ServerStyleSheet
import { ServerStyleSheet } from "styled-components";
export default class MyDocument extends Document {
static getInitialProps({ renderPage }) {
// Step 1: Create an instance of ServerStyleSheet
const sheet = new ServerStyleSheet();
// Step 2: Retrieve styles from components in the page
const page = renderPage(
(App) => (props) => sheet.collectStyles(<App {...props} />)
);
// Step 3: Extract the styles as <style> tags
const styleTags = sheet.getStyleElement();
// Step 4: Pass styleTags as a prop
return { ...page, styleTags };
}
render() {
return (
<html>
<Head>
<title>My app</title>
{/* Step 5: Output the styles in the head */}
{this.props.styleTags}
</Head>
<body>
<Main />
<NextScript />
</body>
</html>
);
}
}
I already tried to install the canary new version (npm install next#11.1.3-canary.15), (saw this solution in another post) but doesn't work.
any tip, please?
This is my Code Which I got from react-p5 typescript Example and modified it a bit
import Sketch from "react-p5";
import p5Types from "p5";
type InputParameterType = {};
function P5JsComponent({}: InputParameterType) {
let x = 50;
const y = 50;
//See annotations in JS for more information
const setup = (p5: p5Types, canvasParentRef: Element) => {
p5.createCanvas(500, 500).parent(canvasParentRef);
};
const draw = (p5: p5Types) => {
p5.background(0);
p5.ellipse(x, y, 70, 70);
x++;
};
return <Sketch setup={setup} draw={draw} />;
}
export default P5JsComponent;
My Parent Component in My NextJs App is 'homepage.tsx' which is present in the pages directory.
import Head from "next/head";
import P5JsComponent from "#/components/P5JsComponent";
function homepage() {
return (
<div>
<Head>
<title>My App</title>
<meta name="description" content="Generated by create next app" />
<link rel="icon" href="/favicon.ico" />
</Head>
<P5JsComponent />
</div>
);
}
export default homepage;
I am getting a ReferenceError: window is not defined error when I run this code.
In server-side-rendering, we haven't global variables from the browser, like the "window" variable.
P5JsComponent must be rendered on the client-side.
Import P5JsComponent with no SSR:
const P5JsComponent = dynamic(
() => import("#/components/P5JsComponent"),
{ ssr: false }
)
ref: https://nextjs.org/docs/advanced-features/dynamic-import
I'm trying to use material-ui with next.js / react.js.
When applying a layout file through _app.js I'm getting an error.
This is using the example react.js template material-ui provides (so confusing why it wouldn't work simply - i get the impression that material-ui has issues with next.js generally?).
I've tried a few different approaches, changing document and app, still won't seem to work.
Here's the Layout.js file
import React from 'react';
import clsx from 'clsx';
import { makeStyles } from '#material-ui/core/styles';
import CssBaseline from '#material-ui/core/CssBaseline';
import Drawer from '#material-ui/core/Drawer';
import List from '#material-ui/core/List';
import Divider from '#material-ui/core/Divider';
import IconButton from '#material-ui/core/IconButton';
import Badge from '#material-ui/core/Badge';
import Container from '#material-ui/core/Container';
import Grid from '#material-ui/core/Grid';
import Paper from '#material-ui/core/Paper';
import Link from '#material-ui/core/Link';
import MenuIcon from '#material-ui/icons/Menu';
import ChevronLeftIcon from '#material-ui/icons/ChevronLeft';
import NotificationsIcon from '#material-ui/icons/Notifications';
import { mainListItems, secondaryListItems } from '../Sidebar/Sidebar';
import Chart from '../Charts/Chart';
import Deposits from '../Deposits';
import Orders from '../Orders';
import Menu from '../AppBar/Menu';
const drawerWidth = 240;
const useStyles = makeStyles(theme => ({
........
export default function Layout() {
const classes = useStyles();
const [open, setOpen] = React.useState(true);
const handleDrawerOpen = () => {
setOpen(true);
};
const handleDrawerClose = () => {
setOpen(false);
};
const fixedHeightPaper = clsx(classes.paper, classes.fixedHeight);
return (
<div className={classes.root}>
<CssBaseline />
<Menu />
<Drawer
variant="permanent"
classes={{
paper: clsx(classes.drawerPaper, !open && classes.drawerPaperClose),
}}
open={open}
>
<div className={classes.toolbarIcon}>
<IconButton onClick={handleDrawerClose}>
<ChevronLeftIcon />
</IconButton>
</div>
<Divider />
<List>{mainListItems}</List>
<Divider />
</Drawer>
{props.children}
</div>
);
}
//<List>{secondaryListItems}</List> later
Heres app.js
import React from 'react';
import App, { Container } from 'next/app';
import Head from 'next/head';
import { ThemeProvider } from '#material-ui/styles';
import CssBaseline from '#material-ui/core/CssBaseline';
import theme from '../src/theme';
import Layout from '../components/Layout/Layout';
class MyApp extends App {
componentDidMount() {
// Remove the server-side injected CSS.
const jssStyles = document.querySelector('#jss-server-side');
if (jssStyles) {
jssStyles.parentNode.removeChild(jssStyles);
}
}
render() {
const { Component, pageProps } = this.props;
return (
<Container>
<Head>
<title>My page</title>
</Head>
<ThemeProvider theme={theme}>
{/* CssBaseline kickstart an elegant, consistent, and simple baseline to build upon. */}
<CssBaseline />
<Layout>
<Component {...pageProps} />
</ Layout>
</ThemeProvider>
</Container>
);
}
}
export default MyApp;
and document.js
import React from 'react';
import Document, { Head, Main, NextScript } from 'next/document';
import { ServerStyleSheets } from '#material-ui/styles';
import theme from '../src/theme';
class MyDocument extends Document {
render() {
return (
<html lang="en">
<Head>
<meta charSet="utf-8" />
{/* Use minimum-scale=1 to enable GPU rasterization */}
<meta
name="viewport"
content="minimum-scale=1, initial-scale=1, width=device-width, shrink-to-fit=no"
/>
{/* PWA primary color */}
<meta name="theme-color" content={theme.palette.primary.main} />
<link
rel="stylesheet"
href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700&display=swap"
/>
</Head>
<body>
<Main />
<NextScript />
</body>
</html>
);
}
}
MyDocument.getInitialProps = async ctx => {
// Resolution order
//
// On the server:
// 1. app.getInitialProps
// 2. page.getInitialProps
// 3. document.getInitialProps
// 4. app.render
// 5. page.render
// 6. document.render
//
// On the server with error:
// 1. document.getInitialProps
// 2. app.render
// 3. page.render
// 4. document.render
//
// On the client
// 1. app.getInitialProps
// 2. page.getInitialProps
// 3. app.render
// 4. page.render
// Render app and page and get the context of the page with collected side effects.
const sheets = new ServerStyleSheets();
const originalRenderPage = ctx.renderPage;
ctx.renderPage = () =>
originalRenderPage({
enhanceApp: App => props => sheets.collect(<App {...props} />),
});
const initialProps = await Document.getInitialProps(ctx);
return {
...initialProps,
// Styles fragment is rendered after the app and page rendering finish.
styles: [
<React.Fragment key="styles">
{initialProps.styles}
{sheets.getStyleElement()}
</React.Fragment>,
],
};
};
export default MyDocument;
And here is the error information.
×
Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons:
1. You might have mismatching versions of React and the renderer (such as React DOM)
2. You might be breaking the Rules of Hooks
3. You might have more than one copy of React in the same app
See https://xxxx-invalid-hook-call for tips about how to debug and fix this problem.
â–¶ 11 stack frames were collapsed.
ctx.renderPage
./pages/_document.js:60
57 | const sheets = new ServerStyleSheets();
58 | const originalRenderPage = ctx.renderPage;
59 |
> 60 | ctx.renderPage = () =>
| ^ 61 | originalRenderPage({
62 | enhanceApp: App => props => sheets.collect(<App {...props} />),
63 | });
View compiled
â–¶ 4 stack frames were collapsed.
This screen is visible only in development. It will not appear if the app crashes in production.
Open your browser’s developer console to further inspect this error.
As far as I am aware you cannot use hooks inside class components and your MyDocument is declared as a class. Maybe try converting it to a functional component.
I solve this problem installing npm i #emotion/react #emotion/styled
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