Next.Js + MUI v5 tutorial
Tags: nextjs, mui, javascript, starter
Published: 2021-12-20 11:49:36

If you are looking for a starter check [Quick Start](#quick-start)
If you also need to use **TypeScript** check [this tutorial](https://dev.to/hajhosein/nextjs-mui-v5-typescript-tutorial-and-starter-3pab)

## Table of contents

- [What is this post about?](#what-is-this-post-about)
- [Why Next.js?](#why-nextjs)
- [Why MUI?](#why-mui)
- [Step one: Installing Next.js](#step-one)
- [Step two: Installing emotion](#step-two)
- [Step three: Installing MUI](#step-three)
- [Step four: Creating a MUI theme](#step-four)
- [Step five: Creating an emotion cache](#step-five)
- [Step six: Edit _app.js](#step-six)
- [Step seven: Creating a custom document file in Next.js](#step-seven)
- [Step eight (Optional but recommended): Using prop-types package](#step-eight)
- [Quick start](#quick-start)
- [Conclusion](#conclusion)

## <a name='what-is-this-post-about'></a> 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.

## <a name='why-nextjs'></a> 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](https://nextjs.org/).

## <a name='why-mui'></a> 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](https://mui.com/) to find more about MUI latest version.

## <a name='step-one'></a> Step one: Installing Next.js

1. Run `npx create-next-app@latest project-name` or `yarn 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`.

2. 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:

![step-01-installing-nextjs-folder-struction](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0un20cgi55lyadx0xbxi.jpg)

3. To make sure that everything works perfectly, run `npm run dev` or `yarn dev` and head over to localhost:3000 in your browser. You should see a page like this.

![step-01-nextjs-welcome-page](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6mweuvoc9gag4g4heyhm.png)

## <a name='step-two'></a> Step two: Installing emotion
as the MUI docs says:
> The default style library used for generating CSS styles for MUI components is [emotion](https://github.com/emotion-js/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`

## <a name='step-three'></a> Step three: Installing MUI
1. install MUI with this command `npm i @mui/material` or in case you use yarn run `yarn add @mui/material`

2. MUI uses [Roboto](https://fonts.google.com/specimen/Roboto) as the default font so you should install that with this command: `npm i @fontsource/roboto` or `yarn add @fontsource/roboto`

3. (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` or `yarn 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-02-package.json](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9hhhlnxt9jpirvdnesz4.jpg)

## <a name='step-four'></a> 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. 

1. create a folder named 'theme' in styles folder 
![step-03-creating-theme-folder](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mykf8o5co7d2u954km3l.jpg)

2. 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: 

```js
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-04-creating-lightTheme-file](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/33yiy6dat2cdk3s7v9py.jpg)

## <a name='step-five'></a> 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:

```js
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 <head> so they're loaded first.

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-05-creating-emotion-cache-function](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8vyyyfybhijyxvz8ss86.jpg)

## <a name='step-six'></a> 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.

1. First open _app.js file in the pages directory.
2. Replace the code with the following one:

```js
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.

## <a name='step-seven'></a> 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.

```js
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.

## <a name='step-eight'></a> 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:

```js
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.

## <a name='quick-start'></a> 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](https://github.com/hajhosein/muxt)

## <a name='conclusion'></a> Conclusion

That's it, your app is now ready. Let me know if you need help or something else.



---
[![A banner of become a backer](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fnb2hbuk8abtl1amqxhi.jpg)](https://hpouyanmehr.com/backers#donate)

Hi! I'm **Hosein Pouyanmehr**. I enjoy sharing what I learn and what I find interesting. Let's connect on [LinkedIn](https://www.linkedin.com/in/hpouyanmehr/).

See my code interests on [GitHub](https://github.com/HPouyanmehr).