Editor’s Note: This post was updated 1 November 2021 to reflect relevant information and updated code blocks.
Material Design is a design language that was first introduced by Google in 2014. It’s a visual language that makes use of grid-based layouts, responsive animations and transitions, padding, and depth effects such as lighting and shadows.
What is Material Design?
The goal for Material Design comes down to three things: create, unify, and customize.
Material Design aims to provide a visual language that synthesizes the classic principles of good design. Unify aims to develop a single underlying system that unifies the user experience across platforms, devices, and input methods, and, for customization, it provides a visual language and a flexible foundation for innovation and brand expression.
In this article, we’ll explore how to create React apps with a Material Design theme. Various libraries help with this, but for this article, we’ll use the material-ui library.
Getting started with Material Design
MUI (previously called Material UI) is a set of React components that implements Google’s Material Design. These components work in isolation, which means they are self-supporting and will inject only the styles they need to display.
To get started with MUI, all you need to do is run these terminal commands:
npm install @mui/material @emotion/react @emotion/styled # to install icon pack: npm install @mui/icons-material
Next, let’s look at some of the fundamentals of MUI as it relates to Material Design and see how it sets up for the rest of the post.
Typography in MUI
When it comes to Material Design, the font choice defaults to Roboto. However, MUI doesn’t ship with the Roboto fault. The font can be imported into a React project using any of the two methods below.
Install from a CDN
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700&display=swap" />
Install with npm
You can install it as an npm package by typing the below command in your terminal:
npm install @fontsource/roboto
Then, you can import it into your project like this:
import '@fontsource/roboto/300.css'; import '@fontsource/roboto/400.css'; import '@fontsource/roboto/500.css'; import '@fontsource/roboto/700.css';
MUI
also provides a component called Typography
. The Typography
component allows you to display text in the app. Here’s an example:
import Typography from "@mui/material/Typography"; //now import CSS files: import "@fontsource/roboto/300.css"; import "@fontsource/roboto/400.css"; import "@fontsource/roboto/500.css"; import "@fontsource/roboto/700.css"; export default function App() { return ( <div className="App"> {/* create h1(title) */} <Typography variant="h1">Use your phone for longer </Typography> {/* create overline */} <Typography variant="overline">Or buy a new one</Typography> </div> ); }
The variant
props help to apply the theme typography styles and color
is the color of the component. It supports theme colors that are suitable for the component.
CSS Baseline
If you’ve been writing frontend code, you should already know about normalize.css
. If not, it’s a collection of HTML element- and attribute style normalizations. MUI provides its own version of normalize.css
, CssBaseline
, a component that provides an elegant, consistent, and simple baseline to build upon.
CSSBaseline
does the following:
- The margin in all browsers is removed
- The default Material Design background color is applied
- Font antialiasing is enabled for better display of the Roboto font
- No base font-size is declared on the
<html>
, but 16px is assumed (browser default)
import CssBaseline from "@mui/material/CssBaseline"; return ( <div> <CssBaseline /> {/* apply normalize.css */} {/* Application code.. */} </div> );
Grid layout in Material Design
Material Design’s responsive UI is based on a 12-column grid layout. This grid creates visual consistency between layouts.
The grid system features the following:
-
- It uses Flexbox
- There are two types of layout: containers and items
- Item widths are set in percentages, so they’re always fluid and sized relative to their parent element
- Items have padding to create the spacing between individual items.
- There are five grid breakpoints: xs, sm, md, lg, and xl
Here’s an example that uses MUI’s Grid
component:
import Grid from "@mui/material/Grid"; //first, create a custom component. It will return a div element with a chosen color. function ColoredComponent({ color }) { return ( <div style={{ backgroundColor: color, width: "100%", paddingTop: "20px", textAlign: "center", }} > <Typography variant="overline"> Sample piece of text</Typography> </div> ); } export default function App() { return ( <div className="App"> {/* Each item will have 1 unit of spacing. The container prop will assign this component as a container*/} <Grid container spacing={1}> {/* on small screens(phones), this item takes up 5 units. On medium-sized screens(tablets), take up 8 units. If an item is too large, it will go to the next line. */} <Grid item xs={5} md={8}> <ColoredComponent color="red" /> </Grid> <Grid item xs={5} md={6}> <ColoredComponent color="blue" /> </Grid> </Grid> </div> );
Icons in React MUI
Icons are a huge part of material design. They are used in buttons to convey action and information. Icons are used to symbolize a command, file, device, or directory.
They are also used to represent actions like trash, print, and save, and are commonly found in app bars, toolbars, buttons, and lists.
Icons in MUI can be rendered using two methods: Icon
for rendering font icons, and SvgIcon
for rendering SVG paths.
The SvgIcon
component takes an SVG path
element as its child and converts it to a React component that displays the path, allowing the icon to be styled and respond to mouse events. This can be seen in the example below:
Material UI also has a package that ships with preset icons to be used in your React app. Here is a list of icons that you can use in your project.
This piece of code will render a fill icon and an outlined one from the Material Icons package:
import FavoriteIcon from "@mui/icons-material/Favorite"; import FavoriteBorderOutlinedIcon from "@mui/icons-material/FavoriteBorderOutlined"; return ( <div> <FavoriteIcon style={{ width: "100%", height: "20px" }} /> <FavoriteBorderOutlinedIcon style={{ width: "100%", height: "20px" }} /> </div> );
Buttons in MUI
Buttons allow users to take action and make choices with a single tap. They help communicate an action a user can take.
Because Material UI
components are in isolation, you’ll need to import the Button
component.
import Button from '@mui/material/Button'; <Button color="success" variant="contained"> Hello World </Button>
The Button
component can take on many props such as color, variant, size, etc. You can check out the full list here.
Buttons in mui
and Material Design at large can take on any of the following forms:
Flat buttons
Flat buttons are buttons with only text. They have no background color and no border. They may be used in dialogs, toolbars, or inline. They do not lift but fill with color when clicked.
Outlined buttons
Outlined buttons are text-only buttons with medium emphasis. They behave like flat buttons but have an outline and are typically used for actions that are important but aren’t the primary action in an app.
Their appearance is usually characterized by a border and a background color on hover.
<Button variant="outlined">Default</Button>
Material-UI components
MUI has several UI components that help us to build a material-designed theme in a React app. Let’s go over some of the mui
components.
App Bars in MUI
The App Bar, formerly known as the action bar in Android, acts as a header navigation menu. It is a toolbar that’s used for branding, navigation, search, and actions. To render a simple App Bar, write this code:
import AppBar from "@mui/material/AppBar"; import Toolbar from "@mui/material/Toolbar"; import Typography from "@mui/material/Typography"; import HomeIcon from "@mui/icons-material/Home"; <div> <AppBar position="static"> <Toolbar> {/* Display a Toolbar at the top */} <Typography> Current page: <HomeIcon /> </Typography> </Toolbar> </AppBar> </div>;
Navigation
Bottom navigation menus are placed at the bottom and they make it easy to switch between top-level views in a single tap.
The navigation menu is characterized by three to five actions, each containing an icon and a label. Although it’s important to note that only navigation bars with three actions should contain both an icon and a label.
import { useState } from "react"; import BottomNavigation from "@mui/material/BottomNavigation"; import BottomNavigationAction from "@mui/material/BottomNavigationAction"; import RestoreIcon from "@mui/icons-material/Restore"; import FavoriteIcon from "@mui/icons-material/Favorite"; import LocationOnIcon from "@mui/icons-material/LocationOn"; const [value, setValue] = useState(0); return ( <div> { /* create the bar */ } <BottomNavigation showLabels value={value} onChange={(event, newValue) => { setValue(newValue); //or change current view }} > <BottomNavigationAction label="Recents" icon={<RestoreIcon />} /> { /*Create an action button with an icon */} <BottomNavigationAction label="Favorites" icon={<FavoriteIcon />} /> <BottomNavigationAction label="Nearby" icon={<LocationOnIcon />} /> </BottomNavigation> <p> Value:{value}</p> {/* displays the current user selection index */ } </div> );
Tabs
Tabs make it easy to explore and switch between different views. A tab component can be a simple tab with no additions, as seen in the first example below, or a scrollable tab with multiple tabs.
A tab can also just consist of icons as titles/labels.
To create a simple tab view, first create a custom component called <code>TabPanel</code>, like so:
import Typography from "@mui/material/Typography"; function TabPanel(props) { const { children, value, index } = props; //if 'value'(current user selection) and 'index'(the corresponding index of the tab) is equal, then display the component return ( <div> {value === index && ( <div> <Typography>{children}</Typography> </div> )} </div> ); }
Next, render your tabs by writing this code:
import Tabs from "@mui/material/Tabs"; import Tab from "@mui/material/Tab"; import { useState } from "react"; export default function App() { const [value, setValue] = useState(0); const handleChange = (event, newValue) => { setValue(newValue); }; return ( <div> <div> <Tabs value={value} onChange={handleChange} //the handleChange method will run when the user clicks on a tab > <Tab label="Item One" /> {/* Create our tabs */} <Tab label="Item Two" /> </Tabs> </div> <TabPanel value={value} index={0}> Item One </TabPanel> <TabPanel value={value} index={1}> Item Two </TabPanel> <Typography>Value: {value} </Typography> </div> ); }
Lists
Lists present multiple line items vertically as a single continuous element.
Lists are made up of a continuous column of rows, and each row contains a tile. Primary actions fill the tile, and supplemental actions are represented by icons and text. This piece of code renders a simple list:
import List from "@mui/material/List"; import ListItem from "@mui/material/ListItem"; import Typography from "@mui/material/Typography"; return ( <div> <Typography variant="h4"> Groceries:</Typography> <List> <ListItem> Eggs</ListItem> <ListItem> Fish</ListItem> <ListItem> Milk</ListItem> </List> </div> ); ``
Cards
A card is a sheet of material that serves as an entry point to more detailed information.
Cards are a convenient means of displaying content composed of different elements. They are used to display information that can be easily glanced at and usually has a call to action (CTA).
import Typography from "@mui/material/Typography"; import CardContent from "@mui/material/CardContent"; import CardActions from "@mui/material/CardActions"; import Card from "@mui/material/Card"; return ( <div> <Card> {/* define the main content of the card */} <CardContent> <Typography variant="h2"> LogRocket Blog</Typography> <Typography variant="overline"> Learn many programming topics from people world-wide </Typography> </CardContent> {/* interact with the card here */} <CardActions> <Typography> <a href="https://blog.logrocket.com/"> Go to blog</a> </Typography> </CardActions> </Card> </div> );
Modal
The Modal component provides a solid foundation for creating dialogs, popovers, lightboxes, etc.
Whenever a modal button is clicked, it does the following:
- Manages dialog stacking when one-at-a-time just isn’t enough
- Creates a backdrop for disabling interaction below the Modal
- It properly manages focus; moving to the modal content, and keeping it there until the modal is closed
- It disables scrolling of the page content while open
- Adds the appropriate ARIA roles are automatically
Let’s use MUI’s Modal component in action. To do so, start by creating a custom dialog box:
import Typography from "@mui/material/Typography"; //Build our custom dialog box: function CustomDialog() { return ( <div> <Typography variant="h2" style={{ backgroundColor: "blue", padding: "20px" }} > This is text is in a dialog box </Typography> </div> ); }
Next, write this code to use the Modal component:
import Modal from "@mui/material/Modal"; import Button from "@mui/material/Button"; import { useState } from "react"; export default function App() { const [open, setOpen] = useState(false); //will control whether the dialog box is open. return ( <div> {/* Set the 'open' Hook to true*/} <Button onClick={() => setOpen(true)}> Open box</Button> {/* The 'open' prop tells whether the dialog box should be rendered*/} {/*The onClose handler will run when the user clicks away from the dialog box*/} <Modal open={open} onClose={() => setOpen(false)}> {/* if 'open' is true, then render the CustomDialog component */} <CustomDialog /> </Modal> </div> ); }
Image list
Image lists are an alternative to standard list views seen above. An image list consists of a repeated pattern of cells arrayed in a vertical and horizontal layout and it usually features images. To create an image list, we use an ImageList
component.
import Typography from "@mui/material/Typography"; import ImageList from "@mui/material/ImageList"; import ImageListItem from "@mui/material/ImageListItem"; //first, define our data source. const images = [ { src: "https://images.pexels.com/photos/1438404/pexels-photo-1438404.jpeg?cs=srgb&dl=pexels-engin-akyurt-1438404.jpg&fm=jpg", alt: "landscape", }, { src: "https://images.pexels.com/photos/7931183/pexels-photo-7931183.jpeg?auto=compress&cs=tinysrgb&dpr=1&w=500", alt: "second image", }, { src: "https://images.pexels.com/photos/1000498/pexels-photo-1000498.jpeg?auto=compress&cs=tinysrgb&dpr=1&w=500", alt: "third image", }, ]; return ( <div> <Typography variant="h4"> My images:</Typography> {/* Now render our images list. It will have 3 columns */} <ImageList sx={{ width: 800, height: 450 }} cols={3} rowHeight={164}> {/* Navigate through the images array. Each item corresponds to an ImageListItem component */} {images.map((item) => ( <ImageListItem> <img src={item.src} alt={item.alt} /> </ImageListItem> ))} </ImageList> </div> );
Tables in React Material
Data tables display sets of raw data. They usually appear in desktop enterprise products. A data table contains a header row at the top that lists column names, followed by rows for data.
For accessibility, the first column is set to an <th>
element, with a scope
of "row"
. This enables screen readers to identify a cell’s value by its row and column name.
A simple usage of tables can be seen in the example below.
import Typography from "@mui/material/Typography"; import Table from "@mui/material/Table"; import TableBody from "@mui/material/TableBody"; import TableCell from "@mui/material/TableCell"; import TableHead from "@mui/material/TableHead"; import TableRow from "@mui/material/TableRow"; import TableContainer from "@mui/material/TableContainer"; return ( <div> <Typography variant="h4"> List of names:</Typography> {/* First, create a container. */} <TableContainer> <Table> {/* Now a Table head. (thead) */} <TableHead> {/*create row */} <TableRow> {/* create cells */} <TableCell>Name</TableCell> <TableCell>Birth year</TableCell> </TableRow> </TableHead> {/* next, create body (tbody) */} <TableBody> <TableRow> <TableCell>Hussain </TableCell> <TableCell> 2002</TableCell> </TableRow> <TableRow> <TableCell> Bill Gates </TableCell> <TableCell>1955 </TableCell> </TableRow> </TableBody> </Table> </TableContainer> </div> );
Conclusion
So, should you use Material Design in your React project?
As usual, the answer to that question depends on your project’s use case and what you’re trying to achieve. Material Design can give you guidelines to make better user interfaces for your applications, and it can inspire your designers to think about the hierarchy of information based on the guidelines.
At the same time, if you’re trying to carry out a brand redesign, Material Design might not be what you’re looking for. Do you want your brand to look like hundreds of other websites on the internet?
You can also explore other Material Design libraries such as react-md or materialize (a CSS framework).
Cut through the noise of traditional React error reporting with LogRocket
LogRocket is a React analytics solution that shields you from the hundreds of false-positive errors alerts to just a few truly important items. LogRocket tells you the most impactful bugs and UX issues actually impacting users in your React applications.

Focus on the React bugs that matter — try LogRocket today.
helpful article 🙂