If you are looking for a starter check Quick Start
If you also need to use TypeScript check this tutorial
Table of contents
- What is this post about?
- Why Next.js?
- Why MUI?
- Step one: Installing Next.js
- Step two: Installing emotion
- Step three: Installing MUI
- Step four: Creating a MUI theme
- Step five: Creating an emotion cache
- Step six: Edit _app.js
- Step seven: Creating a custom document file in Next.js
- Step eight (Optional but recommended): Using prop-types package
- Quick start
- Conclusion
What is this post about?
This post will help you to start a project using Next.js and MUI (formerly Material-UI). As you may notice using Next.js together with MUI may be a little bit tricky. I will explain each step to you to make it much more clear.
Why Next.js?
Well, You can find lots of articles and videos talking about Next.js benefits and I'm not really going to explain everything about Next.js features in this post, but shortly these are some of its amazing features:
It just load the JavaScript and CSS which page really need. This will make the page loading a lot faster.
All images can be optimized with the help of Next.js Image component.
Fast refresh
to discover more about Next.js click here.
Why MUI?
as you can read on MUI site:
MUI provides a robust, customizable, and accessible library of foundational and advanced components, enabling you to build your own design system and develop React applications faster.
MUI is well documented and for every single component you can find out how to use or how to customize it.
the latest version of MUI is faster and also has got a smaller bundle size.
you can click here to find more about MUI latest version.
Step one: Installing Next.js
- Run
npx create-next-app@latest project-name
oryarn create next-app project-name
and replace project-name with your own.
Tip: If you already have a project folder created and wanna install Next.js in that directory simply add period instead of project-name. So, it'll be npx create-next-app@latest .
or yarn create next-app .
I am going to name this starter "muxt" which is combination of MUI and Next. Also I am using NPM as package manager.
so for me it'll be npx create-next-app muxt
.
- After that the installation is done go to the project folder by
cd project-name
and open it in your favorite code editor which for me is VS Code.
the initial folders and files structure should be like this image:
- To make sure that everything works perfectly, run
npm run dev
oryarn dev
and head over to localhost:3000 in your browser. You should see a page like this.
Step two: Installing emotion
as the MUI docs says:
The default style library used for generating CSS styles for MUI components is emotion.
in order to use MUI with Next.js we have to install these packages:
- @emotion/cache
- @emotion/react
- @emotion/server
- @emotion/styled
So, Run npm i @emotion/cache @emotion/react @emotion/server @emotion/styled
or yarn add @emotion/cache @emotion/react @emotion/server @emotion/styled
Step three: Installing MUI
install MUI with this command
npm i @mui/material
or in case you use yarn runyarn add @mui/material
MUI uses Roboto as the default font so you should install that with this command:
npm i @fontsource/roboto
oryarn add @fontsource/roboto
(OPTIONAL) If you think you are going to use MUI Icon components you need to install its package as well, otherwise there is no need to install this package. But I'm going to install it in order to have that in my starter. To do that run
npm i @mui/icons-material
oryarn add @mui/icons-material
Alright, we have installed everything that we need to. Let's take a look at all packages that we installed.
Step four: Creating a MUI theme
After installations, first we need to create a theme. With the help of MUI theme files you can create custom styles or different styles for light or dark mode. Here we are just going to create a theme file with only one option.
create a file with the name of 'lightTheme.js' in the theme folder. The idea behind naming this file lightTheme instead of theme is that we can come back later and add another file with the name of darkTheme which contains our dark mode theme options. in that file add these lines:
import { createTheme } from '@mui/material/styles';
const lightTheme = createTheme({
palette: {
mode: 'light',
},
});
export default lightTheme;
Tip: If you wanna start your app with dark mode you can name the file darkTheme, then set the palette mode to dark and follow next steps with that file.
You may think why we're going to create this file while it has just one option? well,later we need to pass a theme to ThemeProvider component of MUI and the reason that I am put everything in a separate file is that I want to structure files and folders properly from the beginning.
This is how the new folder gonna look like after following steps above:
Step five: Creating an emotion cache
You need to create a cache for your styles because you are using Next.js and this will help the app to figure out what style to apply. For now let's just create a folder name utility and create a file name createEmotionCache.js in that folder.
Then add the following code:
import createCache from '@emotion/cache';
const createEmotionCache = () => {
return createCache({ key: 'css', prepend: true });
};
export default createEmotionCache;
Setting the prepend
key to true
moves MUI styles to the top of the
This is what MUI says about setting prepend
to true
:
It allows developers to easily override MUI styles with other styling solutions, like CSS modules.
Here's the result in editor:
Step six: Edit _app.js
So, until now we've created a lightTheme for our app and we also create a function which create a cache for us, This step and the next one is about using them.
- First open _app.js file in the pages directory.
- Replace the code with the following one:
import React from 'react';
import { CacheProvider } from '@emotion/react';
import { ThemeProvider, CssBaseline } from '@mui/material';
import createEmotionCache from '../utility/createEmotionCache';
import lightTheme from '../styles/theme/lightTheme';
import '../styles/globals.css';
const clientSideEmotionCache = createEmotionCache();
const MyApp = (props) => {
const { Component, emotionCache = clientSideEmotionCache, pageProps } = props;
return (
<CacheProvider value={emotionCache}>
<ThemeProvider theme={lightTheme}>
<CssBaseline />
<Component {...pageProps} />
</ThemeProvider>
</CacheProvider>
);
};
export default MyApp;
Explanation of the code above:
- First we import React
- Next we import CacheProvider component from '@emotion/react', we use this component to provide a shared client-side cache for a user session.
- We also import ThemeProvider and CssBaseline from '@mui/material'; using themeProvider let us to pass our theme throw the app and CssBaseline as mui says:
CssBaseline kickstart an elegant, consistent, and simple baseline to build upon.
- Below the import statements we create a constant which contains a emotion cache and we use it as default value for emotionCache prop.
Step seven: Creating a custom document file in Next.js
In the pages folder add _document.js file. for now just add these lines of code to the file. I'll explain theme in a second.
import * as React from 'react';
import Document, { Html, Head, Main, NextScript } from 'next/document';
import createEmotionServer from '@emotion/server/create-instance';
import createEmotionCache from '../utility/createEmotionCache';
export default class MyDocument extends Document {
render() {
return (
<Html lang="en">
<Head>
<link
rel="stylesheet"
href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700&display=swap"
/>
</Head>
<body>
<Main />
<NextScript />
</body>
</Html>
);
}
}
// `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);
/* eslint-disable */
ctx.renderPage = () =>
originalRenderPage({
enhanceApp: (App) =>
function EnhanceApp(props) {
return <App emotionCache={cache} {...props} />;
},
});
/* eslint-enable */
const initialProps = await Document.getInitialProps(ctx);
// This is important. It prevents emotion to render invalid HTML.
// See https://github.com/mui-org/material-ui/issues/26561#issuecomment-855286153
const emotionStyles = extractCriticalToChunks(initialProps.html);
const emotionStyleTags = emotionStyles.styles.map((style) => (
<style
data-emotion={`${style.key} ${style.ids.join(' ')}`}
key={style.key}
// eslint-disable-next-line react/no-danger
dangerouslySetInnerHTML={{ __html: style.css }}
/>
));
return {
...initialProps,
// Styles fragment is rendered after the app and page rendering finish.
styles: [
...React.Children.toArray(initialProps.styles),
...emotionStyleTags,
],
};
};
Ok, time to explain what exactly going to happen.
- On the first line we import React
- On the second line we import Document, HTML, Head, Main, NextScript
- We extend our custom Document component with imported Document from 'next/document'. Generally the purpose is to have everything from Document component by default and then customize something in it.
- Imported Html component help us to set some properties like lang or dir for our app.
- Imported Head component is useful if you wanna have some general thing in you app, for example you can import your app icon here. Just be aware that this component is different from the one that we can import from 'next/head'
- In addition to Html and Head component, Main and NextScript are also required for the page to render properly.
- Next, when we use getInitialProps we enable server-side rendering and it let us to have initial data population. as the Next.js docs says:
it means sending the page with the data already populated from the server.
Step eight (Optional but recommended): Using prop-types package
It is a good practice to provide types for our props to avoid runtime errors and also make development easier. Since we don't use typescript in this starter, we can use "props-types" package to define type and enable runtime type checking for our app.
So, run npm i prop-types
or yarn add prop-types
.
after that open _app.js and replace the code with this one:
import React from 'react';
import PropTypes from 'prop-types';
import { CacheProvider } from '@emotion/react';
import { ThemeProvider, CssBaseline } from '@mui/material';
import createEmotionCache from '../utility/createEmotionCache';
import lightTheme from '../styles/theme/lightTheme';
import '../styles/globals.css';
const clientSideEmotionCache = createEmotionCache();
const MyApp = (props) => {
const { Component, emotionCache = clientSideEmotionCache, pageProps } = props;
return (
<CacheProvider value={emotionCache}>
<ThemeProvider theme={lightTheme}>
<CssBaseline />
<Component {...pageProps} />
</ThemeProvider>
</CacheProvider>
);
};
export default MyApp;
MyApp.propTypes = {
Component: PropTypes.elementType.isRequired,
emotionCache: PropTypes.object,
pageProps: PropTypes.object.isRequired,
};
What we do here is that we define some keys and their type which can be accessible through props of MyApp component.
Quick start
If you feel this process a bit boring or if you want to start faster you can check the link below which is the result of steps that I explained above. Just download the code and rename the project to whatever you want and run npm i
or yarn
.
get starter from here
Conclusion
That's it, your app is now ready. Let me know if you need help or something else.
Hi! I'm Hosein Pouyanmehr. I enjoy sharing what I learn and what I find interesting. Let's connect on LinkedIn.
See my code interests on GitHub.
Top comments (22)
Nice article! Keep it going
An extension to this article will be - how effectively can we css using next js - if you see right now we got a bunch of ways to write the css and there is no definitive guide to do that
Food for thought!
cheers
skt
My pleasure
gog
Thank you Hossein. Your post helped me alot. Keep writing :)
I'm really glad that it was useful for you.
Nice article! I noticed that it's loosely based on the official example as well.
The only problem with this approach is that this set up alone produces ~80kb of shared JS, which is a little scary considering that bundles of about ~100kb or more are already considered heavy by Next.js itself.
By the way, I didn't really noticed any difference (apart from increased bundle size) after adding the recommended set-up. Do you have any idea on why is this needed conceptually?
I think the Idea is that you want to include the styles into the first Response - that means in the original Document your browser recieves.
If you don't do it like that, you can experience flickering when first visiting the page. Depending on how much your page depends on the styles this can be more or less apparent.
As with everywhere, you are facing a trade off here. Do you want to have the styles immediatly on visit? Then you have to accept the larger bundle size.
That sounds about to be right (or, at least, very aligned with what MUI itself says).
What's interesting is that I tried to load my page with JS disabled & without this set-up.
100% of styles were present on the initial page load.
I actually did not read the MUI page before, so i feel very good about my answer haha :D
I think the styles are not loaded via javascript but via style tags. So it should not matter that much if js is enabled or not.
Styles being present on initial page load - im sceptical about this. Maybe its just very fast and you dont notice the "flicker". You can try to throttle network speed on your browser. Or fire a request via postman and see if the actual styles are in the response you see there.
Without the setup the styles should be missing, but there should be a style tag that will load the styles as soon as the browser interprets it.
If the styles are already there, you uncoverd a next.js mystery here ^^
High quality article.
Thx.
You're welcome.
hossein jan faghat ye soali, tadakholi ba chizi nadare in code ha?
سلام به شما
مواردی که گفته شد، تنظیماتی هستن که برای عملکرد صحیح امیوآی داخل نکست ضروری هستن و تداخلی با چیزی ایجاد نمیکنه؛ توی کدها چیزی دور زده نشده که منجر به تداخل بشه و در حقیقت یه مسیر جدید برای عملکرد نکست تعریف شده که کَشِ مربوط به امیوآی رو در هر دو سمت کلاینت و سرور یکسان نگهاش داریم.
با این حال اگه جایی به مشکلی برخوردی یا تداخلی ایجاد شده بود از نظرت، منو در جریان بذار، خوشحال میشم کمکت کنم.
ممنون ازت حسین جان. من تازه نکست رو شروع کردم و چیزی از بک اند نمیدونم و هر آموزشی که میبینم یک سری کدهای بک هم داره که من اصلا متوجه نمیشم و گرفتن دیتا مثل ری اکت راحت نیست. نکست واقعا جزو مهارت های فرانت محسوب میشه؟
خواهش میکنم
یکی از دلایلی که باعث شد من بیام سمت نکست این بود که عیب و نقص های ریاکت رو نداشت، یه سری موارد داخلش باعث میشه که دیگه نیازی به نصب و کانفیگ پکیجهای اضافه نباشه. نکست جی اس در کل یه پله فراتر از ریاکته چون نواقصی که ریاکت داره از جمله موارد مربوط به سئو رو به خوبی حل میکنه و اگه میخوای توی بازار کار موفقتر باشی به نظرم نکست میتونه بهت کمک کنه چون که داخل کشور خودمون علاقه به نکست روز به روز بیشتر میشه.
برای اینکه بتونی با بکاند کار کنی نیازی نیست کاملاً مسلط به اتفاقای اون سمت باشی، ارتباط با بکاند هم میتونه به شیوههای مختلفی داخل نکست جی اس پیاده بشه و حتی میتونی همون روشای ریاکت رو اینجا هم پیاده کنی.
از اونجایی که میگی تازه استارت زدی نکست رو این چیزا طبیعیه زیاد نگران این نباش که همه مسائل رو متوجه نمیشی اول کار، اولش برا همه سخته؛ حتی اگه چندین سال هم نکست کار کنی بازم به مسائلی بر میخوری که برات جدید باشن و ندونی.
من که از فنای نکست محسوب میشم 😂و هرچی بیشتر باهاش کار میکنم بیشتر متوجه قدرتش و انعطافپذیریش میشم. اگه دوست داشتی آموزش زبان اصلی بهت معرفی کنم یه راه ارتباطی برام بذار من سعی میکنم هم راجع به نکست هم ریاکت و هم بکاند برات موارد باحالیو بفرستم.
آره خیلی عالی میشه بهم معرفی کنی من همینجوری دارم جسته گریخته از جاهای مختلف یاد میگیرمش مثل همین مقاله ی خودت که تو سرچم بهش برخوردم. این آی دی تلگراممه حسین جان @parham_kns
it's not used anymore? Sorry, all this stuff is kinda confusing.
It should be nice an updated tutorial. Thanks!
Hey Ricardo, Thanks for reading. I hope to find some good free time. This article needs an update as there are updates on Next.js.
Hi ... Nice article
Would you explain the same code for app router? Of course, without using 'use client' !
Hey Reza, Thanks for your feedback.
I need some free time to update this article or post another one to meet the "app" folder of Next.js. I'll let you know about that as soon as it was published.
Shortly about the frequent 'use client' convention in Next.js, as each component is a server component by default, and you have to keep the styles consice between client and server to avoid issues in MUI and Next.js, you need to use that phrase in some cases like the MUI theme related components. However you don't need to use that keyword everywhere as the MUI docs mentioned.
I will clarify where to use and where not to use the 'use client' in that article.