From ae83448cfc104d49d24f3d6f30519480f4c7fbe0 Mon Sep 17 00:00:00 2001 From: Marija Najdova Date: Fri, 3 Jun 2022 13:50:21 +0200 Subject: [PATCH 1/5] [examples] Add examples using nextjs with @mui/styles as a starter for migration to v5 --- .../.gitignore | 37 +++++ .../README.md | 48 +++++++ .../next-env.d.ts | 5 + .../next.config.js | 4 + .../package.json | 31 +++++ .../pages/_app.tsx | 40 ++++++ .../pages/_document.tsx | 130 ++++++++++++++++++ .../pages/about.tsx | 43 ++++++ .../pages/index.tsx | 40 ++++++ .../public/favicon.ico | Bin 0 -> 25931 bytes .../src/Copyright.tsx | 15 ++ .../src/Link.tsx | 102 ++++++++++++++ .../src/ProTip.tsx | 32 +++++ .../src/createEmotionCache.ts | 7 + .../src/theme.ts | 19 +++ .../tsconfig.json | 21 +++ .../types/mui-styles.d.ts | 5 + 17 files changed, 579 insertions(+) create mode 100644 examples/nextjs-with-typescript-v4-migration/.gitignore create mode 100644 examples/nextjs-with-typescript-v4-migration/README.md create mode 100644 examples/nextjs-with-typescript-v4-migration/next-env.d.ts create mode 100644 examples/nextjs-with-typescript-v4-migration/next.config.js create mode 100644 examples/nextjs-with-typescript-v4-migration/package.json create mode 100644 examples/nextjs-with-typescript-v4-migration/pages/_app.tsx create mode 100644 examples/nextjs-with-typescript-v4-migration/pages/_document.tsx create mode 100644 examples/nextjs-with-typescript-v4-migration/pages/about.tsx create mode 100644 examples/nextjs-with-typescript-v4-migration/pages/index.tsx create mode 100644 examples/nextjs-with-typescript-v4-migration/public/favicon.ico create mode 100644 examples/nextjs-with-typescript-v4-migration/src/Copyright.tsx create mode 100644 examples/nextjs-with-typescript-v4-migration/src/Link.tsx create mode 100644 examples/nextjs-with-typescript-v4-migration/src/ProTip.tsx create mode 100644 examples/nextjs-with-typescript-v4-migration/src/createEmotionCache.ts create mode 100644 examples/nextjs-with-typescript-v4-migration/src/theme.ts create mode 100644 examples/nextjs-with-typescript-v4-migration/tsconfig.json create mode 100644 examples/nextjs-with-typescript-v4-migration/types/mui-styles.d.ts 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..dce8ce358e4469 --- /dev/null +++ b/examples/nextjs-with-typescript-v4-migration/README.md @@ -0,0 +1,48 @@ +# Next.js with TypeScript example with @mui/styles + +## 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) + +[![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) + +## 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 MUI 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 configuring both emotion & JSS for server side rendering. +The project is intended as a basic starter while you are migration your application from v4 to v5, as it allows the JSS style overrides to take presendence over the styles coming by default for the Material UI components. + +## 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 MUI. +More information [in the documentation](https://mui.com/material-ui/guides/routing/#next-js). + +## What's next? + + + +You now have a working example project. +You can head back to the documentation, continuing browsing it from the [templates](https://mui.com/material-ui/getting-started/templates/) section. 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..609016efe8e358 --- /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": "^5.0.0", + "@mui/material": "^5.0.0", + "@mui/styles": "^5.0.0", + "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..80550b95f438a7 --- /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); + + // Gemerate style tags for the styles coming from emotion + // This is important. It prevents emotion to render invalid HTML. + // See https://github.com/mui/material-ui/issues/26561#issuecomment-855286153 + const emotionStyles = extractCriticalToChunks(initialProps.html); + const emotionStyleTags = emotionStyles.styles.map((style) => ( +