React Material UI
Introduction
Material UI is one of the most popular UI libraries for React, implementing Google's Material Design guidelines. It provides a comprehensive set of pre-built, customizable React components that help developers build beautiful and consistent user interfaces quickly.
In this guide, we'll explore how to integrate Material UI into a React application, understand its core concepts, and learn how to use its components to create modern, responsive interfaces without having to write extensive CSS from scratch.
Why Use Material UI?
Material UI offers several advantages for React developers:
- Ready-to-use components: Buttons, forms, navigation, and more that follow design best practices
- Consistent styling: Components that work well together with a cohesive look and feel
- Responsive design: Built-in mobile-first approach
- Customization: Ability to override default styles and create custom themes
- Accessibility: Built with accessibility in mind
- Active community: Regular updates and extensive documentation
Getting Started with Material UI
Installation
First, you'll need to install Material UI in your React project:
npm install @mui/material @emotion/react @emotion/styled
# or
yarn add @mui/material @emotion/react @emotion/styled
For icons (optional but recommended):
npm install @mui/icons-material
# or
yarn add @mui/icons-material
Basic Setup
Once installed, you can start using Material UI components in your React application. Let's create a simple example:
import React from 'react';
import Button from '@mui/material/Button';
function App() {
return (
<div>
<Button variant="contained" color="primary">
Hello Material UI
</Button>
</div>
);
}
export default App;
This will render a primary-colored Material UI button with contained styling:
Core Concepts
The ThemeProvider
Material UI uses React's context API to provide a theme to all components. Wrap your application with ThemeProvider
to ensure consistent styling:
import React from 'react';
import { createTheme, ThemeProvider } from '@mui/material/styles';
import App from './App';
// Create a theme
const theme = createTheme({
palette: {
primary: {
main: '#1976d2',
},
secondary: {
main: '#dc004e',
},
},
});
function ThemedApp() {
return (
<ThemeProvider theme={theme}>
<App />
</ThemeProvider>
);
}
export default ThemedApp;
Styling Components
Material UI offers multiple approaches to styling:
- Predefined Props: Using the
sx
prop for one-off styling - Styled API: Using the styled components approach
- Theme customization: Modifying the global theme
Using the sx
Prop
The sx
prop is a shortcut for defining custom styles that has access to the theme:
import Box from '@mui/material/Box';
function StyledComponent() {
return (
<Box
sx={{
width: 300,
height: 100,
backgroundColor: 'primary.main',
'&:hover': {
backgroundColor: 'primary.dark',
opacity: [0.9, 0.8, 0.7],
},
}}
>
This Box has custom styling
</Box>
);
}
Using the Styled API
For more complex components, you can use the styled API:
import { styled } from '@mui/material/styles';
import Button from '@mui/material/Button';
const CustomButton = styled(Button)(({ theme }) => ({
backgroundColor: theme.palette.primary.main,
padding: theme.spacing(2),
'&:hover': {
backgroundColor: theme.palette.primary.dark,
},
}));
function StyledButtonExample() {
return <CustomButton>Custom Styled Button</CustomButton>;
}
Essential Material UI Components
Let's explore some of the most commonly used components in Material UI.
Buttons and Variants
Material UI offers different button variants:
import Button from '@mui/material/Button';
import Stack from '@mui/material/Stack';
function ButtonVariants() {
return (
<Stack spacing={2} direction="row">
<Button variant="text">Text</Button>
<Button variant="contained">Contained</Button>
<Button variant="outlined">Outlined</Button>
</Stack>
);
}
Input Fields and Forms
Creating forms with Material UI is straightforward:
import React, { useState } from 'react';
import TextField from '@mui/material/TextField';
import Button from '@mui/material/Button';
import Box from '@mui/material/Box';
function SimpleForm() {
const [formData, setFormData] = useState({
name: '',
email: '',
});
const handleChange = (e) => {
setFormData({
...formData,
[e.target.name]: e.target.value
});
};
const handleSubmit = (e) => {
e.preventDefault();
console.log('Form submitted:', formData);
};
return (
<Box component="form" onSubmit={handleSubmit} sx={{ '& > :not(style)': { m: 1 } }}>
<TextField
name="name"
label="Full Name"
variant="outlined"
value={formData.name}
onChange={handleChange}
fullWidth
/>
<TextField
name="email"
label="Email"
variant="outlined"
type="email"
value={formData.email}
onChange={handleChange}
fullWidth
/>
<Button type="submit" variant="contained" color="primary">
Submit
</Button>
</Box>
);
}
Cards for Content Display
Cards are a great way to display content:
import Card from '@mui/material/Card';
import CardActions from '@mui/material/CardActions';
import CardContent from '@mui/material/CardContent';
import CardMedia from '@mui/material/CardMedia';
import Button from '@mui/material/Button';
import Typography from '@mui/material/Typography';
function ProductCard() {
return (
<Card sx={{ maxWidth: 345 }}>
<CardMedia
component="img"
height="140"
image="/product-image.jpg"
alt="product"
/>
<CardContent>
<Typography gutterBottom variant="h5" component="div">
Product Title
</Typography>
<Typography variant="body2" color="text.secondary">
This is a description of the product. It explains the features
and benefits of purchasing this item.
</Typography>
</CardContent>
<CardActions>
<Button size="small">Share</Button>
<Button size="small">Learn More</Button>
</CardActions>
</Card>
);
}
Navigation Components
Material UI provides components for creating navigation interfaces:
import React, { useState } from 'react';
import AppBar from '@mui/material/AppBar';
import Box from '@mui/material/Box';
import Toolbar from '@mui/material/Toolbar';
import Typography from '@mui/material/Typography';
import Button from '@mui/material/Button';
import IconButton from '@mui/material/IconButton';
import MenuIcon from '@mui/icons-material/Menu';
import Drawer from '@mui/material/Drawer';
import List from '@mui/material/List';
import ListItem from '@mui/material/ListItem';
import ListItemText from '@mui/material/ListItemText';
function NavigationExample() {
const [drawerOpen, setDrawerOpen] = useState(false);
const toggleDrawer = (open) => (event) => {
if (
event.type === 'keydown' &&
(event.key === 'Tab' || event.key === 'Shift')
) {
return;
}
setDrawerOpen(open);
};
return (
<Box sx={{ flexGrow: 1 }}>
<AppBar position="static">
<Toolbar>
<IconButton
size="large"
edge="start"
color="inherit"
aria-label="menu"
sx={{ mr: 2 }}
onClick={toggleDrawer(true)}
>
<MenuIcon />
</IconButton>
<Typography variant="h6" component="div" sx={{ flexGrow: 1 }}>
My Application
</Typography>
<Button color="inherit">Login</Button>
</Toolbar>
</AppBar>
<Drawer
anchor="left"
open={drawerOpen}
onClose={toggleDrawer(false)}
>
<Box
sx={{ width: 250 }}
role="presentation"
onClick={toggleDrawer(false)}
onKeyDown={toggleDrawer(false)}
>
<List>
{['Home', 'Products', 'About', 'Contact'].map((text) => (
<ListItem button key={text}>
<ListItemText primary={text} />
</ListItem>
))}
</List>
</Box>
</Drawer>
</Box>
);
}
Building a Responsive Layout
Material UI provides Grid
and Box
components for creating responsive layouts:
import Grid from '@mui/material/Grid';
import Box from '@mui/material/Box';
import Paper from '@mui/material/Paper';
import { styled } from '@mui/material/styles';
// Create styled component
const Item = styled(Paper)(({ theme }) => ({
backgroundColor: theme.palette.mode === 'dark' ? '#1A2027' : '#fff',
...theme.typography.body2,
padding: theme.spacing(1),
textAlign: 'center',
color: theme.palette.text.secondary,
}));
function ResponsiveGridLayout() {
return (
<Box sx={{ flexGrow: 1 }}>
<Grid container spacing={2}>
<Grid item xs={12} sm={6} md={4}>
<Item>xs=12 sm=6 md=4</Item>
</Grid>
<Grid item xs={12} sm={6} md={4}>
<Item>xs=12 sm=6 md=4</Item>
</Grid>
<Grid item xs={12} sm={6} md={4}>
<Item>xs=12 sm=6 md=4</Item>
</Grid>
</Grid>
</Box>
);
}
In this example, the grid items will be:
- One column on extra small screens (xs)
- Two columns on small screens (sm)
- Three columns on medium and larger screens (md)
Creating a Custom Theme
You can customize Material UI's default theme to match your brand:
import { createTheme, ThemeProvider } from '@mui/material/styles';
import CssBaseline from '@mui/material/CssBaseline';
// Create a custom theme
const theme = createTheme({
palette: {
primary: {
main: '#3f51b5',
},
secondary: {
main: '#f50057',
},
background: {
default: '#f5f5f5',
},
},
typography: {
fontFamily: [
'Roboto',
'"Helvetica Neue"',
'Arial',
'sans-serif',
].join(','),
h1: {
fontSize: '2.5rem',
fontWeight: 500,
},
button: {
textTransform: 'none',
},
},
shape: {
borderRadius: 8,
},
});
function ThemedApp({ children }) {
return (
<ThemeProvider theme={theme}>
<CssBaseline /> {/* Provides baseline CSS normalization */}
{children}
</ThemeProvider>
);
}
Real-world Example: Product Listing Page
Let's build a real-world example of a product listing page using Material UI:
import React, { useState } from 'react';
import {
Container, Grid, Card, CardMedia, CardContent, CardActions,
Typography, Button, AppBar, Toolbar, TextField, InputAdornment,
Box, Rating, Chip
} from '@mui/material';
import SearchIcon from '@mui/icons-material/Search';
import ShoppingCartIcon from '@mui/icons-material/ShoppingCart';
// Sample product data
const products = [
{
id: 1,
name: 'Wireless Headphones',
price: 129.99,
rating: 4.5,
image: '/headphones.jpg',
category: 'Electronics'
},
{
id: 2,
name: 'Running Shoes',
price: 89.99,
rating: 4.2,
image: '/shoes.jpg',
category: 'Sports'
},
{
id: 3,
name: 'Coffee Maker',
price: 59.99,
rating: 3.8,
image: '/coffee-maker.jpg',
category: 'Home'
},
// More products...
];
function ProductListingPage() {
const [searchTerm, setSearchTerm] = useState('');
const filteredProducts = products.filter(product =>
product.name.toLowerCase().includes(searchTerm.toLowerCase())
);
return (
<Box>
<AppBar position="static">
<Toolbar>
<Typography variant="h6" sx={{ flexGrow: 1 }}>
MyShop
</Typography>
<Button color="inherit" startIcon={<ShoppingCartIcon />}>
Cart (0)
</Button>
</Toolbar>
</AppBar>
<Container sx={{ mt: 4 }}>
<TextField
fullWidth
margin="normal"
placeholder="Search products..."
variant="outlined"
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
InputProps={{
startAdornment: (
<InputAdornment position="start">
<SearchIcon />
</InputAdornment>
),
}}
/>
<Typography variant="h4" component="h1" sx={{ my: 4 }}>
Products {searchTerm && `matching "${searchTerm}"`}
</Typography>
<Grid container spacing={4}>
{filteredProducts.map((product) => (
<Grid item key={product.id} xs={12} sm={6} md={4}>
<Card sx={{ height: '100%', display: 'flex', flexDirection: 'column' }}>
<CardMedia
component="img"
height="200"
image={product.image}
alt={product.name}
/>
<CardContent sx={{ flexGrow: 1 }}>
<Typography gutterBottom variant="h5" component="h2">
{product.name}
</Typography>
<Box sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', mb: 2 }}>
<Typography variant="h6" color="primary">
${product.price.toFixed(2)}
</Typography>
<Chip label={product.category} size="small" />
</Box>
<Box sx={{ display: 'flex', alignItems: 'center' }}>
<Rating value={product.rating} precision={0.5} readOnly />
<Typography variant="body2" sx={{ ml: 1 }}>
{product.rating}
</Typography>
</Box>
</CardContent>
<CardActions>
<Button
size="small"
variant="contained"
startIcon={<ShoppingCartIcon />}
sx={{ marginLeft: 'auto' }}
>
Add to Cart
</Button>
</CardActions>
</Card>
</Grid>
))}
</Grid>
</Container>
</Box>
);
}
This example demonstrates how Material UI components can be combined to create a complete interface that is both functional and responsive.
Advanced Topics
Server-side Rendering
Material UI supports server-side rendering. Here's a basic setup for Next.js:
// pages/_document.js
import * as React from 'react';
import Document, { Html, Head, Main, NextScript } from 'next/document';
import { ServerStyleSheets } from '@mui/styles';
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>
);
}
}
// Collect styles for SSR
MyDocument.getInitialProps = async (ctx) => {
const sheets = new ServerStyleSheets();
const originalRenderPage = ctx.renderPage;
ctx.renderPage = () =>
originalRenderPage({
enhanceApp: (App) => (props) => sheets.collect(<App {...props} />),
});
const initialProps = await Document.getInitialProps(ctx);
return {
...initialProps,
styles: [
...React.Children.toArray(initialProps.styles),
sheets.getStyleElement(),
],
};
};
Performance Optimization
To optimize performance with Material UI, consider the following:
- Import components directly rather than from the main package:
// Better for performance
import Button from '@mui/material/Button';
// Less efficient
import { Button } from '@mui/material';
- Use dynamic imports for large components not needed immediately:
import React, { Suspense, lazy } from 'react';
const HeavyComponent = lazy(() => import('./HeavyComponent'));
function MyComponent() {
return (
<Suspense fallback={<div>Loading...</div>}>
<HeavyComponent />
</Suspense>
);
}
Common Pitfalls and Solutions
Too Many Re-renders
// Problem: Inline object creation causes unnecessary re-renders
<Button sx={{ margin: 2, padding: 1 }}>Click Me</Button>
// Solution: Define styles outside of render
const buttonStyles = { margin: 2, padding: 1 };
<Button sx={buttonStyles}>Click Me</Button>
Unexpected Theme Behavior
// Problem: Using theme without ThemeProvider
<Box sx={{ color: theme.palette.primary.main }}>Text</Box>
// Solution: Ensure ThemeProvider is wrapping your component
<ThemeProvider theme={theme}>
<Box sx={{ color: 'primary.main' }}>Text</Box>
</ThemeProvider>
Summary
Material UI is a powerful React component library that implements Google's Material Design. It provides:
- A comprehensive set of pre-built, customizable components
- Consistent styling and theming capabilities
- Responsive design out of the box
- Advanced styling options through various APIs
- Good accessibility practices
With Material UI, developers can create professional-looking interfaces quickly without sacrificing customization or performance. The library's component-based approach aligns well with React's philosophy, making it a popular choice for React developers.
Additional Resources
To further explore Material UI:
Exercises
- Create a sign-up form with Material UI form components that includes validation.
- Build a responsive dashboard layout with AppBar, Drawer, and multiple Cards.
- Customize the Material UI theme to match a specific brand's color scheme.
- Create a data table with sorting, filtering, and pagination using Material UI's DataGrid component.
- Build a photo gallery with Material UI's ImageList component that works well on both desktop and mobile devices.
By practicing these exercises, you'll gain confidence using Material UI and be able to create sophisticated interfaces for your React applications.
If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)