One of the most popular React component libraries is Material UI (MUI). It has ready-made parts that follow Google's Material Design rules. This guide will show you how to add MUI to your Next.js or React project.
How to Add MUI to a React Project
If you want to add Material UI to a standard React app made with Vite or Create React App, do these things.
Setting Up the Core Packages
Install MUI and all of its peer dependencies:
# Using npm
npm install @mui/material @emotion/react @emotion/styled
# Using yarn
yarn add @mui/material @emotion/react @emotion/styled
Installing Fonts (Optional)
The Roboto font looks best with MUI parts. Install it as a package:
# Using npm
npm install @fontsource/roboto
# Using yarn
yarn add @fontsource/roboto
In your main.tsx or index.tsx file, add the following lines to import the font weights:
import '@fontsource/roboto/300.css';
import '@fontsource/roboto/400.css';
import '@fontsource/roboto/500.css';
import '@fontsource/roboto/700.css';
Installing Icons (Optional)
To gain access to the full icon library, add the Material Icons package:
# Using npm
npm install @mui/icons-material
# Using yarn
yarn add @mui/icons-material
Setting Up the Basic Theme
Make a theme.ts file to set up your colour scheme and other changes:
import { createTheme } from '@mui/material/styles';
const theme = createTheme({
palette: {
primary: {
main: '#1976d2',
},
secondary: {
main: '#dc004e',
},
},
});
export default theme;
Putting the Theme into Action
In App.tsx or main.tsx, wrap your application in ThemeProvider:
import CssBaseline from '@mui/material/CssBaseline';
import { ThemeProvider } from '@mui/material/styles';
import theme from './theme';
function App() {
return (
<ThemeProvider theme={theme}>
<CssBaseline />
{/* The parts of your app */}
</ThemeProvider>
);
}
export default App;
CssBaseline makes sure that all browsers use the same styles and defaults.
Using Components
In your React files, import and use MUI components:
import { Box, Button, TextField, Typography } from '@mui/material';
function MyComponent() {
return (
<Box>
<Typography variant="h1">Page Title</Typography>
<TextField label="Name" />
<Button variant="contained">Save</Button>
</Box>
);
}
Additional Packages
Date Pickers
Add the date adapter to the date picker package:
# Using npm
npm install dayjs @mui/x-date-pickers
# Using yarn
yarn add dayjs @mui/x-date-pickers
Put LocalizationProvider around your app:
import { LocalizationProvider } from '@mui/x-date-pickers';
import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs';
function App() {
return <LocalizationProvider dateAdapter={AdapterDayjs}>{/* Your app */}</LocalizationProvider>;
}
Data Grid
For tables that can be sorted, filtered, and paged:
# Using npm
npm install @mui/x-data-grid
# Using yarn
yarn add @mui/x-data-grid
Adding MUI to a Next.js Project
Because of server-side rendering, Next.js needs more setup. The App Router needs to do something special with Emotion's CSS cache.
Installing Packages
Set up the MUI Next.js package for integration:
npm install @emotion/react @emotion/styled @mui/material
npm install @mui/material-nextjs
npm install @mui/icons-material
Setting Up the Theme Provider
Make a client component in src/app/providers.tsx:
'use client';
import { PropsWithChildren } from 'react';
import { AppRouterCacheProvider } from '@mui/material-nextjs/v13-appRouter';
import { ThemeProvider } from '@mui/material/styles';
import theme from '@/styles/themes';
const Providers = ({ children }: PropsWithChildren) => {
return (
<AppRouterCacheProvider>
<ThemeProvider theme={theme}>{children}</ThemeProvider>
</AppRouterCacheProvider>
);
};
export default Providers;
The AppRouterCacheProvider takes care of Emotion's CSS cache for the App Router. This stops hydration mismatches between the server and the client rendering.
Theme Structure
A well-organised structure for your theme makes it easier to keep up with your style. Make a folder for your themes with separate files for colours, fonts, breakpoints, and component overrides.
This is how to organise your theme files:
src/styles/themes/
├── index.ts # Main theme file
├── colors.ts # Color definitions
├── palette.ts # MUI palette mapping
├── typography.ts # Typography variants
├── breakpoints.ts # Responsive breakpoints
└── components.ts # Component overrides
The Main Theme File
Your index.ts file puts everything together:
'use client';
import { createTheme } from '@mui/material';
import breakpoints from './breakpoints';
import components from './components';
import palette from './palette';
import typography from './typography';
const theme = createTheme({
breakpoints,
components,
palette,
typography,
});
export default theme;
Setting Up Your Colour System
Make a file called colors.ts that has all of your colours. Put colours into families and rate their intensity from 50 (lightest) to 950 (darkest).
// colors.ts
const colors = {
// Neutral greys
graytone950: '#161616',
graytone900: '#1f1f1f',
graytone400: '#888888',
graytone50: '#f6f6f6',
// Semantic colours
blue500: '#4190ff', // info/links
green500: '#14b575', // success
orange500: '#fa541c', // error/warning
// Base
white: '#fff',
black: '#111',
};
export default colors;
Setting Up the Palette
Assign your colours to MUI's semantic palette roles. This lets MUI components use your colours without you having to do anything.
// palette.ts
import { ThemeOptions } from '@mui/material';
import colors from './colors';
const palette: ThemeOptions['palette'] = {
primary: {
main: colors.graytone950,
contrastText: colors.graytone50,
},
secondary: {
main: colors.graytone50,
contrastText: colors.graytone950,
},
error: {
main: colors.orange500,
},
success: {
main: colors.green500,
},
info: {
main: colors.blue500,
},
};
export default palette;
Setting Up Typography
Set up typography variants so that all the text in your app looks the same. Add responsive changes to headings.
// typography.ts
import { TypographyVariantsOptions } from '@mui/material';
const typography: TypographyVariantsOptions = {
fontFamily: "'Inter', sans-serif",
h1: {
fontSize: '64px',
fontWeight: 500,
lineHeight: '98%',
'@media (max-width: 768px)': {
fontSize: '48px',
},
},
body1: {
fontSize: '16px',
lineHeight: 'normal',
},
body2: {
fontSize: '14px',
lineHeight: '100%',
},
button: {
fontSize: '14px',
fontWeight: 600,
textTransform: 'none',
},
};
export default typography;
By default, MUI will not change the case of button text if you set textTransform to 'none'.
Component Overrides
You can change the default styles for any component with MUI. This lets you completely control how parts look and work.
Turning Off Ripple Effects
To make the interface look better, turn off ripple effects for all buttons:
MuiButtonBase: {
defaultProps: {
disableTouchRipple: true,
disableRipple: true
}
}
Custom Button Styles
Make buttons with backgrounds that change colour and effects when you hover over them:
MuiButton: {
defaultProps: {
variant: 'contained',
size: 'large'
},
styleOverrides: {
root: {
borderRadius: 16,
boxShadow: 'none',
'&:hover': {
boxShadow: 'none'
}
}
}
}
Best Practices for Styling
When using MUI, pick the right way to style for each situation.
Use MUI props for layout: Use props like direction, spacing, alignItems, and justifyContent to set up your layout. Content works well as props for components.
Use CSS or SCSS modules for visual styling: Stylesheets should include colors, backgrounds, borders, and animations. This keeps your styles organized and your JSX clean.
Use the classes prop for specific changes: The classes prop lets you change the styles of specific MUI components without using !important.
Avoid the sx prop: The sx prop makes it easy to quickly prototype things, but it also mixes styling logic into your components. CSS modules help you keep your styles organised and your concerns separate.
Performance Tips
A few simple changes can make a big difference in performance:
- Create the theme object once – Using it in multiple renders saves time and money by not having to do the same calculations over and over again.
- Turn off ripple effects – This makes the DOM less heavy and speeds up how quickly interactions are handled.
- Use gradients instead of box shadows – Gradients tend to load faster because they use GPU acceleration.
- Use CSS modules – This limits your styles to specific parts of your site, cuts down on the amount of CSS that needs to be sent, and stops style conflicts.
- Use font-display: swap – Load custom fonts from your public directory. This makes sure that text stays visible while fonts are loading.








