概要
NextjsでMaterial-UIとStyledComponentsを適用しようとしたところ、リロード時に一瞬Material-UIの表示がされる事象が発生した件について。
修正内容
_document.tsxの{materialUiSheets.getStyleElement()}と{styledComponentsSheet.getStyleElement()}の適用順序を修正することで、事象が解消した。
修正前
return {
...initialProps,
styles: [
<React.Fragment key="styles">
{initialProps.styles}
{styledComponentsSheet.getStyleElement()}
{materialUiSheets.getStyleElement()}
</React.Fragment>,
],
};
修正後
return {
...initialProps,
styles: [
<React.Fragment key="styles">
{initialProps.styles}
{materialUiSheets.getStyleElement()}
{styledComponentsSheet.getStyleElement()}
</React.Fragment>,
],
};
全てのコードは以下の通り。
_document.tsx
import { ServerStyleSheets as MaterialServerStyleSheets } from "@material-ui/core";
import { RenderPageResult } from "next/dist/next-server/lib/utils";
import NextDocument, {
DocumentContext,
DocumentInitialProps,
Head,
Html,
Main,
NextScript,
} from "next/document";
import React from "react";
import { ServerStyleSheet } from "styled-components";
export default class CustomDocument extends NextDocument {
static async getInitialProps(
ctx: DocumentContext
): Promise<DocumentInitialProps> {
const styledComponentsSheet = new ServerStyleSheet();
const materialUiSheets = new MaterialServerStyleSheets();
const originalRenderPage = ctx.renderPage;
try {
ctx.renderPage = (): RenderPageResult | Promise<RenderPageResult> =>
originalRenderPage({
enhanceApp: (App) => (
props
): React.ReactElement<{
sheet: ServerStyleSheet;
}> =>
styledComponentsSheet.collectStyles(
materialUiSheets.collect(<App {...props} />)
),
});
const initialProps = await NextDocument.getInitialProps(ctx);
return {
...initialProps,
styles: [
<React.Fragment key="styles">
{initialProps.styles}
{materialUiSheets.getStyleElement()}
{styledComponentsSheet.getStyleElement()}
</React.Fragment>,
],
};
} finally {
styledComponentsSheet.seal();
}
}
render(): React.ReactElement {
return (
<Html lang="ja-JP">
<Head>
<meta charSet="utf-8" />
<meta name="theme-color" content="#5C6BC0" key="themeColor" />
<link rel="icon" href="/favicon.ico" />
</Head>
<body>
<Main />
<NextScript />
</body>
</Html>
);
}
}
参考までに、_app.tsxと.babelrcは以下の通りです。
_app.tsx
import theme from "@/styles/theme";
import CssBaseline from "@material-ui/core/CssBaseline";
import {
StylesProvider,
ThemeProvider as MaterialUIThemeProvider,
} from "@material-ui/styles";
import "modern-css-reset/dist/reset.min.css";
import Head from "next/head";
import React, { useEffect } from "react";
import { ThemeProvider as StyledComponentsThemeProvider } from "styled-components";
const MyApp = ({ Component, pageProps }: any): JSX.Element => {
// Remove the server-side injected CSS.(https://material-ui.com/guides/server-rendering/)
useEffect(() => {
const jssStyles = document.querySelector("#jss-server-side");
if (jssStyles && jssStyles.parentNode) {
jssStyles.parentNode.removeChild(jssStyles);
}
}, []);
return (
<StylesProvider injectFirst>
<MaterialUIThemeProvider theme={theme}>
<StyledComponentsThemeProvider theme={theme}>
<Head>
<meta
name="viewport"
content="width=device-width,height=device-height"
key="viewport"
/>
</Head>
<CssBaseline />
<Component {...pageProps} />
</StyledComponentsThemeProvider>
</MaterialUIThemeProvider>
</StylesProvider>
);
};
export default MyApp;
.babelrc
{
"presets": [
"next/babel"
],
"plugins": [
[
"babel-plugin-styled-components",
{
"ssr": true
}
]
]
}
以上
以上です。