diff --git a/docs/data/material/getting-started/example-projects/example-projects.md b/docs/data/material/getting-started/example-projects/example-projects.md index 9a3d39826c4f7f..f819dc2fa1f546 100644 --- a/docs/data/material/getting-started/example-projects/example-projects.md +++ b/docs/data/material/getting-started/example-projects/example-projects.md @@ -18,6 +18,7 @@ You can find some example projects in the [GitHub repository](https://github.com - [Tailwind CSS](https://github.com/mui/material-ui/tree/master/examples/tailwind-css) - [Vite.js](https://github.com/mui/material-ui/tree/master/examples/vitejs) - [Use styled-components as style engine](https://github.com/mui/material-ui/tree/master/examples/create-react-app-with-styled-components) ([TypeScript version](https://github.com/mui/material-ui/tree/master/examples/create-react-app-with-styled-components-typescript)) +- [Next.js + @mui/styles (v4 migration helper)](https://github.com/mui/material-ui/tree/master/examples/nextjs-with-typescript-v4-migration) Create React App is an awesome project for learning React. Have a look at [the alternatives available](https://github.com/facebook/create-react-app/blob/HEAD/README.md#popular-alternatives) to see which project best fits your needs. diff --git a/docs/data/material/migration/migration-v4/migrating-from-jss.md b/docs/data/material/migration/migration-v4/migrating-from-jss.md index 7d19b7a3b65a45..96ee4b3ea01c41 100644 --- a/docs/data/material/migration/migration-v4/migrating-from-jss.md +++ b/docs/data/material/migration/migration-v4/migrating-from-jss.md @@ -17,6 +17,10 @@ One of the biggest changes in v5 is the replacement of JSS for [Emotion](https:/ Note that you may continue to use JSS for adding overrides for the components (e.g. `makeStyles`, `withStyles`) even after migrating to v5. Then, if at any point you want to move over to the new styling engine, you can refactor your components progressively. +:::info +If you are using Next.js and you are not sure how to configure SSR to work with both Emotion & JSS, take a look a this [example project](https://github.com/mui/material-ui/tree/master/examples/nextjs-with-typescript-v4-migration). +::: + This document reviews all the steps necessary to migrate away from JSS. While you can use either of the following two options, the first is considered preferable: diff --git a/docs/data/material/migration/migration-v4/migration-v4.md b/docs/data/material/migration/migration-v4/migration-v4.md index 0fa26d74295b6c..92aea69c39ee5e 100644 --- a/docs/data/material/migration/migration-v4/migration-v4.md +++ b/docs/data/material/migration/migration-v4/migration-v4.md @@ -27,6 +27,10 @@ This process is covered in [Migrating from JSS](/material-ui/migration/migrating Need to refer back to an older version of the docs? Check out [the v4 documentation here](https://v4.mui.com/). ::: +:::info +If you are using Next.js and you are not sure how to configure SSR to work with both Emotion & JSS, take a look a this [example project](https://github.com/mui/material-ui/tree/master/examples/nextjs-with-typescript-v4-migration). +::: + ## Why you should migrate Material UI v5 includes many bug fixes and improvements over v4. diff --git a/examples/nextjs-with-typescript-v4-migration/.gitignore b/examples/nextjs-with-typescript-v4-migration/.gitignore new file mode 100644 index 00000000000000..0a5247fbb5ad45 --- /dev/null +++ b/examples/nextjs-with-typescript-v4-migration/.gitignore @@ -0,0 +1,37 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +/node_modules +/.pnp +.pnp.js + +# testing +/coverage + +# next.js +/.next/ +/out/ + +# production +/build + +# misc +.DS_Store +*.pem + +# debug +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# local env files +.env.local +.env.development.local +.env.test.local +.env.production.local + +# vercel +.vercel + +# typescript +*.tsbuildinfo \ No newline at end of file diff --git a/examples/nextjs-with-typescript-v4-migration/README.md b/examples/nextjs-with-typescript-v4-migration/README.md new file mode 100644 index 00000000000000..f453b64decf861 --- /dev/null +++ b/examples/nextjs-with-typescript-v4-migration/README.md @@ -0,0 +1,42 @@ +# Material UI v5 and Next.js example with @mui/styles (in TypeScript) + +## How to use + +Download the example [or clone the repo](https://github.com/mui/material-ui): + + + +```sh +curl https://codeload.github.com/mui/material-ui/tar.gz/master | tar -xz --strip=2 material-ui-master/examples/nextjs-with-typescript +cd nextjs-with-typescript +``` + +Install it and run: + +```sh +npm install +npm run dev +``` + +or: + + + +[![Edit on StackBlitz](https://developer.stackblitz.com/img/open_in_stackblitz.svg)](https://stackblitz.com/github/mui/material-ui/tree/master/examples/nextjs-with-typescript-v4-migration) + +[![Edit on CodeSandbox](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/s/github/mui/material-ui/tree/master/examples/nextjs-with-typescript-v4-migration) + +## The idea behind the example + +The project uses [Next.js](https://github.com/vercel/next.js), which is a framework for server-rendered React apps. +It includes `@mui/material` and its peer dependencies, including `emotion`, the default style engine in Material UI v5. If you prefer, you can [use styled-components instead](https://mui.com/material-ui/guides/interoperability/#styled-components). +It also includes `@mui/styles`, the legacy styling solution that uses JSS as an engine. +It provides all the necessary config for working with both Emotion and JSS for server-side rendering. +The project is intended as a basic starter for migrating your application from v4 to v5, as it lets the JSS style overrides take precedence over the default styles passed to the components by Emotion. +It demonstrates what results after handling v5's breaking changes to the [theme](https://mui.com/material-ui/migration/v5-style-changes/) and [components](https://mui.com/material-ui/migration/v5-component-changes/). + +## The Link component + +Next.js has [a custom Link component](https://nextjs.org/docs/api-reference/next/link). +The example folder provides adapters for usage with Material UI. +You can find more information [in the documentation](https://mui.com/material-ui/guides/routing/#next-js). diff --git a/examples/nextjs-with-typescript-v4-migration/next-env.d.ts b/examples/nextjs-with-typescript-v4-migration/next-env.d.ts new file mode 100644 index 00000000000000..4f11a03dc6cc37 --- /dev/null +++ b/examples/nextjs-with-typescript-v4-migration/next-env.d.ts @@ -0,0 +1,5 @@ +/// +/// + +// NOTE: This file should not be edited +// see https://nextjs.org/docs/basic-features/typescript for more information. diff --git a/examples/nextjs-with-typescript-v4-migration/next.config.js b/examples/nextjs-with-typescript-v4-migration/next.config.js new file mode 100644 index 00000000000000..3dd7ef15191a9f --- /dev/null +++ b/examples/nextjs-with-typescript-v4-migration/next.config.js @@ -0,0 +1,4 @@ +/** @type {import('next').NextConfig} */ +module.exports = { + reactStrictMode: true, +}; diff --git a/examples/nextjs-with-typescript-v4-migration/package.json b/examples/nextjs-with-typescript-v4-migration/package.json new file mode 100644 index 00000000000000..cd2f52c75d43bc --- /dev/null +++ b/examples/nextjs-with-typescript-v4-migration/package.json @@ -0,0 +1,31 @@ +{ + "name": "nextjs-with-typescript-with-mui-styles", + "version": "5.0.0", + "private": true, + "scripts": { + "dev": "next dev", + "build": "next build", + "start": "next start", + "lint": "next lint", + "post-update": "echo \"codesandbox preview only, need an update\" && yarn upgrade --latest" + }, + "dependencies": { + "@emotion/cache": "^11.7.1", + "@emotion/react": "^11.9.0", + "@emotion/server": "^11.4.0", + "@emotion/styled": "^11.8.1", + "@mui/icons-material": "latest", + "@mui/material": "latest", + "@mui/styles": "latest", + "next": "12.1.5", + "react": "^17.0.2", + "react-dom": "^17.0.2" + }, + "devDependencies": { + "@types/node": "latest", + "@types/react": "17.0.2", + "eslint": "latest", + "eslint-config-next": "latest", + "typescript": "latest" + } +} diff --git a/examples/nextjs-with-typescript-v4-migration/pages/_app.tsx b/examples/nextjs-with-typescript-v4-migration/pages/_app.tsx new file mode 100644 index 00000000000000..f71405f34ad4ed --- /dev/null +++ b/examples/nextjs-with-typescript-v4-migration/pages/_app.tsx @@ -0,0 +1,40 @@ +import * as React from 'react'; +import Head from 'next/head'; +import { AppProps } from 'next/app'; +import { ThemeProvider } from '@mui/material/styles'; +import CssBaseline from '@mui/material/CssBaseline'; +import { CacheProvider, EmotionCache } from '@emotion/react'; +import theme from '../src/theme'; +import createEmotionCache from '../src/createEmotionCache'; + +// Client-side cache, shared for the whole session of the user in the browser. +const clientSideEmotionCache = createEmotionCache(); + +interface MyAppProps extends AppProps { + emotionCache?: EmotionCache; +} + +export default function MyApp(props: MyAppProps) { + const { Component, emotionCache = clientSideEmotionCache, pageProps } = props; + + React.useEffect(() => { + // Remove the server-side injected CSS. + const jssStyles = document.querySelector('#jss-server-side'); + if (jssStyles) { + jssStyles?.parentElement?.removeChild(jssStyles); + } + }, []); + + return ( + + + + + + {/* CssBaseline kickstart an elegant, consistent, and simple baseline to build upon. */} + + + + + ); +} diff --git a/examples/nextjs-with-typescript-v4-migration/pages/_document.tsx b/examples/nextjs-with-typescript-v4-migration/pages/_document.tsx new file mode 100644 index 00000000000000..eff5c47e478538 --- /dev/null +++ b/examples/nextjs-with-typescript-v4-migration/pages/_document.tsx @@ -0,0 +1,130 @@ +import * as React from 'react'; +import Document, { Html, Head, Main, NextScript } from 'next/document'; +import createEmotionServer from '@emotion/server/create-instance'; +import { ServerStyleSheets as JSSServerStyleSheets } from '@mui/styles'; +import theme from '../src/theme'; +import createEmotionCache from '../src/createEmotionCache'; + +export default class MyDocument extends Document { + render() { + return ( + + + {/* PWA primary color */} + + + + {/* Inject MUI styles first to match with the prepend: true configuration. */} + {(this.props as any).emotionStyleTags} + + +
+ + + + ); + } +} + +// You can find a benchmark of the available CSS minifiers under +// https://github.com/GoalSmashers/css-minification-benchmark +// We have found that clean-css is faster than cssnano but the output is larger. +// Waiting for https://github.com/cssinjs/jss/issues/279 +// 4% slower but 12% smaller output than doing it in a single step. +// +// It's using .browserslistrc +let prefixer: any; +let cleanCSS: any; +if (process.env.NODE_ENV === 'production') { + /* eslint-disable global-require */ + const postcss = require('postcss'); + const autoprefixer = require('autoprefixer'); + const CleanCSS = require('clean-css'); + /* eslint-enable global-require */ + + prefixer = postcss([autoprefixer]); + cleanCSS = new CleanCSS(); +} + +// `getInitialProps` belongs to `_document` (instead of `_app`), +// it's compatible with static-site generation (SSG). +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 + + const originalRenderPage = ctx.renderPage; + + // You can consider sharing the same emotion cache between all the SSR requests to speed up performance. + // However, be aware that it can have global side effects. + const cache = createEmotionCache(); + const { extractCriticalToChunks } = createEmotionServer(cache); + const jssSheets = new JSSServerStyleSheets(); + + ctx.renderPage = () => + originalRenderPage({ + enhanceApp: (App: any) => + function EnhanceApp(props) { + return jssSheets.collect(); + }, + }); + + const initialProps = await Document.getInitialProps(ctx); + + // Generate style tags for the styles coming from Emotion + // This is important. It prevents Emotion from rendering invalid HTML. + // See https://github.com/mui/material-ui/issues/26561#issuecomment-855286153 + const emotionStyles = extractCriticalToChunks(initialProps.html); + const emotionStyleTags = emotionStyles.styles.map((style) => ( +