Taking Material-UI for a Spin
Material-UI started back in 2014 to unify React and Material Design.
Today, Material-UI has grown to become one of the world’s most popular React UI libraries — backed by a vibrant community of more than 1M developers in over 180 countries.
Their mission is to provide components to make the development of a React UI easier and faster. They had me at easier, so I decided to take it out for a spin! For this example, I’ll be referring to the Material-UI framework as MUI. I am using a simple React app with no styling. The app displays a counter with 2 buttons “up” and “down”. The counter goes up by one when the “up” button is clicked and down by one when the “down” button is clicked. Currently, it looks like this on the browser.
App.js file looks like this.
CounterInput component is fairly basic.
As you can see, we are using the standard
h1 tags in our components. Let’s try to give this a bit more life. I’d like for it to have a bar on top with the name, maybe some icons, nicer buttons, and be responsive. Ok, I think that should be good for now. Let’s get to it.
1 - Install Necessary MUI Packages
We can use either NPM or Yarn to install our packages.
// With npm
// core gives us access to the core components of MUI
npm install @material-ui/core// icons gives us access to a ton of icons
npm install @material-ui/icons// styles gives us access to things like the makeStyles hook and themes provider
npm install @material-ui/styles// With yarn
yarn add @material-ui/core
yarn add @material-ui/icons
yarn add @material-ui/styles
Once installed our
package.json file should look similar to this.
2 - Import Fonts
When using MUI, you are responsible for importing the fonts used in your application even when using the default theme. We will be using the default theme so we will import the Roboto font. MUI will not auto-import the Roboto font for you so be sure to do this.
// In your index.html file inside the head tag add
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700&display=swap" />
3 - Create Layout
This can be a bit confusing so I will break it down into a few steps.
For this layout, we will be using the
Grid component from MUI. The goal here is to have one large container that has 2 items inside of it displayed in one column — the
appBar on top and our
CounterInput component below it.
3.1 — Import Grid
Gridat the top of our
App.js file and remove all the code from inside our
3.2 — Structure Grid Components
In order to achieve the structure we want we need to use a few attributes provided to us by the
Grid API . In MUI, we use the
container boolean attribute to ensure our
Grid adopts the container behavior. We will set the direction using the
direction= attribute with the direction value set equal to
'column’. This way the items inside will appear on top of each other.
We continue by adding the
Grid container that will hold our
appBar as well as the one that will hold our
CounterInput component. In MUI, when adding a
Grid within a
Grid we must identify the inner
item attribute. In our case, we need another container because the
appBar will have other items within. MUI allows us to have containers within containers by combining the
Here is what it should look like on the browser.
Now, we want to make sure this is centered so we want to add some space between the left edge of the screen and do the same on the right side of the screen. We can do that by adding some more
Let’s break this down. We added 3 new
Grid item components inside our second
Grid container item. We’ve added 2 self-closing
Grid item components — lines 14 and 16 will create the spaces we wanted on either side of our
3.3 — Add Breakpoints For Responsive Features
MUI was built with a Mobile-First design in mind. This means that our breakpoints work from the smallest size up. Here is a good article describing this design strategy.
From the Material-UI docs.
The grid creates visual consistency between layouts while allowing flexibility across a wide variety of designs. Material Design’s responsive UI is based on a 12-column grid layout.
How it works
The grid system is implemented with the
- It uses CSS’s Flexible Box module for high flexibility.
- 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.
NOTE: The example above is emulating a small size screen.
On the first row, we have one item with a breakpoint of
xs=12. This means that the item will occupy 12 columns on screen sizes extra small and above. On the second row, we see two items with a breakpoint of
xs=12 sm=6. This means that the item will occupy 12 columns on extra small screens and occupy only 6 columns on screen sizes small and above. On the third row, we have 4 items that each take up 3 columns on small screens and 6 columns on extra small screens. Once we shrink the screen we can see the result.
Ok, so we’ve added the breakpoints in lines 17 through 21 above that will cause spaces to appear on either side of our CounterInput in screens that are small and above and will take them away when the screen size is extra small.
On line 18, we indicate that we want this grid to take up all 12 columns on extra small screens and to take up only 8 columns on screens with sizes small and above.
This should look a bit different on the browser.
It may not look centered if we don’t have enough data to display. If we add some more we can see the space we are actually using.
Now, we have extra spaces on both sides when the screen is small or larger.
If it is extra small we have no spaces and we can move on to the next step.
4 - Build The Header Component
From within your project directory, run the following command in your terminal to create the new file.
Inside this file, add the following code.
Now, we need to import the following components from MUI:
You can simply start adding code and React will auto-import for us.
Since this is our
AppBar we want it to be there all the time. We can do this by using the
position= attribute and set it equal to
5 - Import Header Component
Now, we just need to import this in the
Here, we’ve imported the
Header component on line 4 and added it inside our first
Grid container item on line 13. It should look something like this on the browser.
You may see a space between the AppBar and the browser search bar when using MUI. There is a quick fix for this. In our
index.html file we can add the following code inside the first
<body style='margin : 0'>
It should look like this:
On the browser you should see this:
6 - Add Some Icons To App Bar
Ok, we have our
AppBar ready. Now we want to spruce it up with some icons.
Browse all available icons in the
@material-ui/icons package by visiting the Material Icons search page and find the one you want to use. Click on the icon and copy the import statement provided in the pop-up.
Open up the Header component and paste in the import statement and add the selected icon component inside our
It should now look like this on the browser.
Hmm… I’d like that icon to be on the opposite end of the
makeStyles(hook generator) and
withStyles(HOC) APIs allow the creation of multiple style rules per style sheet. Each style rule has its own class name. The class names are provided to the component with the
classesvariable. This is particularly useful when styling nested elements in a component.
Sweet, let’s use the
makeStyles hook generator to get that icon where we want it. We want to import
makeStyles and create a constant called
useStyles and give it a value of the
makeStyles() function. We pass our style object in as an argument.
You should now see that the icon is now where we want it.
7 - Add MUI Buttons To CounterInput
CounterInput component is using basic
button tags for the buttons but we want to spruce up our component with some MUI dopeness.
Let’s add our CounterImput component to
App.js so we can see our buttons.
We’ve imported the component on line 3 and are returning it on line 19 above. Now, we should be able to see our
CounterInput on the browser.
Let’s give these bland buttons some love. I’d also like to make this component a bit cooler perhaps use a card and some styling for the text. We’ve imported the
Button component from MUI Core and replaced our existing
button tags with it. We are going to use the
makeStyles() hook generator again so we want to convert this class component to a functional component.
We’re going from this.
It should look like this on the browser.
There are a few options for the type of button you use. The options are outlined, contained, and text which is the default. To choose one of the options we use the
variant= attribute in the Button.
If we want to change the color, we can use the
color= attribute and choose from the available options listed as primary, secondary, disabled, and link or we can use the
makeStyles hook generator as we did before to override default styles.
These are the available colors in the default theme.
We’ll use the outlined variant with the color attribute set to primary.
On the browser, we should see our new buttons.
Yells Very Nice!! in Borat's voice…
Cool, so one thing I noticed is that our buttons no longer work as expected. Now, each time I click the “up” button I get a negative number, and when I click the “down” button the negative number increases. NOOOOOOOO…
To me, that meant that only one of the conditions in the
if statement inside the
handleOnClick() function is triggering.
Our event listener was looking for the event target name or
e.target.name . To investigate I added
console.log(e.target.name) inside the
handleOnClick() function. With that, I learned that the target name was coming up as undefined.
Ok, so if the target name is undefined what happened to our target? I removed the name from the console log and looked at the target itself
console.log(e.target). I was a bit surprised by my discovery.
Ok…Where did this come from? Well… At least it has the button name correctly. It turns out that MUI creates these for each
innerText attribute to access the name of the button in my console.log(e.target.innerText) and voila!
Now that we have what we need we update our function accordingly.
Woot Woot! We got over that hurdle pretty easy. Now we can move along to styling this component.
8 - Add Card Components And Styling
Now we just need to import all the components we need and pass them in our return statement. The
Card component has a few other components that we will need to structure properly within one another as follows.
<Card> <CardHeader title="title goes here"/> <CardContent>
buttons go here
We’ve imported all the necessary card components and refactored our return statement to include our components with the correct structure. You should see this on the browser.
As mentioned, we will use the
makeStyles() hook generator again. We import it then create the function for our hook and add a variable called
classes and make it equal to the
useStyles() function and pass it our props. Then we are able to use the
className= attribute to override the default styling with our own. Also, we want to make the counter bigger so we can use the
component= attribute to change the type to an
h1 for example.
You should see this on the browser.
And just like that we are done! Whew, I hope that wasn’t too long for ya! Here is a quick video showing the working app.
I really hope this was fun, helpful and informative. I’d love to hear your feedback in the comments. Stay healthy, stay curious!