9.1 React Design
# MUI (Material-UI)
As we started talking about last week, MUI is a great system for building and styling your React Apps. Used in conjunction with the emotion library (opens new window) styling system, it comes with lots of great pre-built components, styling methods, palettes, colours, hooks, and themes.
It is a large system with a lot of different elements to use but the best part is that it can be used incrementally. Not everything in your app has to be an MUI Component. And you can use your own CSS files and classes in combination with MUI.
It is important to note that we are using version 5 of MUI. Versions 1 through 4 were called Material-UI
. They had a different domain, a different package name and all the imports were in different folders.
The simplest swap is for the component imports.
//v4
import Button from '@material-ui/core/Button';
//v5
import Button from '@mui/material/Button';
2
3
4
Note the change of the first two parts of the import path. This change is the same for ALL components.
All the version 5 imports will start with @mui/
. Most of them start with @mui/material
. We will see a couple others below but the differences will be highlighted in the example.
# MUI Components
Here is the list of supported components in MUI (opens new window). On the MUI site, to reference the Components and see examples of all the variants, be sure to go to the Components
section of the menu. This list is grouped by purpose type.
If you are just looking for the list of attributes/properties that you can add to a Component, then go to the Component API
section of the left-hand menu.
Components are meant to be used in place of the html-style JSX elements that you write in your React applications.
A few guidelines:
- If you want to add some text, use a
<Typography>
component. - If you want a div-like container, use a
<Box>
component. - Some components, like
<Button variant="contained" startIcon={<DeleteIcon/>}>Delete</Button>
have properties that let you load another component inside them. - Material Icon Font is a font used for the icons. This means all CSS font properties like size and color apply to the icons.
The search tool on the MUI website is an excellent way to navigate the huge amount of resources on their site.
# MUI Styling
The styled()
method can be imported from @mui/material
.
import { styled } from '@mui/system';
It is a great way to build a styled version of a Component for your current page. It builds on top of the default theme from MUI or on top of a theme object that you create yourself. Styled reference page (opens new window)
//method signature
styled(Component, [options])(styles) => Component
2
From the method signature we can see that styled()
accepts a base Component (along with its theme styling) and an optional options object. It returns a function that gets called and passed a styles
object. It returns your new styled Component
.
It is worth noting that this is NOT the only way of styling components. There is also:
- the
sx
prop, which will be discussed in the theming section. - the
style
tagged template literal approach fromemotion
. styled-component
which is part of the alternate import shown in the first MUI content in week 7.
Here is a simple example of the method being used to build a styled div.
const CustomDiv = styled('div')({
color: 'darkslategray',
backgroundColor: 'aliceblue',
padding: 8,
borderRadius: 4,
});
export default function MyComponent() {
return (
<main>
<CustomDiv>Styled div</CustomDiv>
</main>
);
}
2
3
4
5
6
7
8
9
10
11
12
13
14
# MUI Themes
MUI comes with a default theme, which is a collection of a range of CSS properties intended to style all the components. You can also use these theme values to style your own components.
# Theme Provider
Themes use React Context to work. This means that we need to create a Theme Provider and put that up near the top level of our app. Note the import path.
import { createTheme, ThemeProvider } from '@mui/system';
This will give use the ThemeProvider
component to put in our JSX.
export default function App() {
return (
<ThemeProvider theme={myTheme}>
<SomeAppStuffAndMyOtherComponents />
</ThemeProvider>
);
}
2
3
4
5
6
7
The ThemeProvider
has a theme property that needs to be given a theme object. We use the createTheme
method to create the theme object to pass into the Provider.
//outside the App function
const myTheme = createTheme({
palette: {
primary: {
main: '#123abc',
contrastText: '#eee',
},
},
});
2
3
4
5
6
7
8
9
The default Theme is passed in and merged with the theme object we are creating. See the default theme here (opens new window).
Now your theme will be stored as a Context that we can access from other files (Components).
# useTheme Hook
To access the custom theme from inside any other page/file/component we need to import the useTheme
hook.
import { useTheme } from '@mui/material/styles';
export default function MyThing() {
const theme = useTheme();
console.log(theme); //see the theme object
//we can extract values from the theme.
return (
<div>
<h1>{theme.spacing}</h1>
<h2>{theme.palette.mode}</h2>
<h3>{theme.palette.primary.main}</h3>
</div>
);
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# Theme with styled()
We can access the values from the custom theme when using the styled
method too. The example below will be passed an object and the theme object get destructured from that. Then in the styles object we are creating we can access the values from the theme, like theme.palette.primary.main
.
const MyThemeComponent = styled('div')(({ theme }) => ({
color: theme.palette.primary.contrastText,
backgroundColor: theme.palette.primary.main,
padding: theme.spacing(1),
borderRadius: theme.shape.borderRadius,
}));
2
3
4
5
6
# The sx prop
With your MUI Components you can use the sx
prop to set the values for various style properties. These will override the default values from MUI. For most elements you will have to add the colours and amount of spacing/padding you want to use.
See the sx prop reference (opens new window) in the System section of the left hand menu.
The sx
prop accepts an object with style properties and values. the values are strings or numbers. Many of the strings are references to values inside the theme. This works even without the useTheme
hook in your component.
<Typography
sx={{
color: 'success.dark',
display: 'inline',
fontWeight: 'medium',
fontSize: 3
px: 1,
}}
>
2
3
4
5
6
7
8
9
In the above example, the color
property accepts success.dark
as the value. This is a shortcut to theme.palette.success.dark
. The color property is mapped to look inside theme.palette
. The fontWeight
property is using the string medium
which refers to the value in theme.typography.fontWeightMedium
. The fontSize
property is using the number 3. This is actually referencing an array index. In the theme being used for this example the theme.typography.fontSize
value is an array with a type scale. [12, 14, 20, 24, 36, 48]. So the Typography component is going to use 20px
as the font size. If, in your theme you have a single value saved as the fontSize then that will be used by default. The px
value is also pointing to an array index.
Padding and margin can be referenced through m
, mx
, my
, mt
, mb
, mr
, ml
, p
, px
, py
, pt
, pb
, pr
, or pl
. Which refer to margin or padding on all sides, in the x direction, y direction, top, bottom, right or left.
# Spacing in a Theme
Some Components have a spacing
prop that you can use to set the space between child components. The Grid Component (opens new window) is an example of an element using the spacing
prop.
When creating your theme object, the value for spacing
can be a number or a function. The default is the number 8
. So, when you use the spacing
prop in your component it will be your value multiplied by 8px.
You can override that recommended default value like this:
const theme = createTheme({
spacing: 6,
});
2
3
Or you can set the value of spacing as a function to calculate the value.
const theme = createTheme({
spacing: (factor) => `${0.5 * factor}rem`,
});
2
3
The value of factor
is coming from your spacing
prop. It will be multiplied by 0.5
and set as an rem
value for the component.
OR alternatively you can pass in an array of values. Then the value in the spacing
prop will be used as an array index.