How to Add Custom Props to Material-UI Typography Component

Zymantas Katinas
Geek Culture
Published in
5 min readMay 14, 2021

Before we start, here is the end result we will achieve by the end of this article:

✔️ A Typography component with custom fontWeight and emphasis props. JavaScript React Codesandbox

In this article, I use JavaScript just for simplicity, but I have included the Typescript version as well. Typescript React Codesandbox

💡 Also if you are looking for a one-time solution, here’s a quick and easy way to override the font-weight using the Box component:

<Typography>
<Box fontWeight="fontWeightBold">
This text is bold
</Box>
</Typography>

The proper way

Before you create any custom properties that will override default styles, let’s see why sometimes it’s better to leave it as it is.

Material-UI makes it easy to keep your design consistent and gives you a way to override the styles for specific variants of Typography. Which most of the time should have the same look and feel throughout the project.

const theme = createMuiTheme({
typography: {
h1: {
fontWeight: 700,
fontSize: 20,
},
},
})

Now if you wrap your project with the custom theme created, all the h1 elements will have the same style.

<ThemeProvider theme={theme}>
<Typography variant='h1'>
Bold heading, 20px size
</Typography>
</ThemeProvider>

However, there are times when you want to have a little more control over the specific text elements without changing your main configuration or adding additional wrappers.

You could use an inline style prop to change whatever you need, but that reduces the consistency greatly and makes it hard to control the design clutter as your project grows.

My solution was to override the Typography component and add some additional props.

Let’s start with the fontWeight first.

Adding fontWeight property

import {
makeStyles,
Typography as MuiTypography,
} from '@material-ui/core'
const useStyles = makeStyles({
overrides: {
fontWeight: ({ fontWeight }) => fontWeight,
},
})
const Typography = ({ fontWeight, children }) => {
const classes = useStyles({ fontWeight })
return (
<MuiTypographyclassName={classes.overrides}>
{children}
</MuiTypography>
)
}
export default Typography

As you can see we are extending the Material Typography component with our own wrapper. It allows us to pass custom props.

đź’ˇ importing Typography as MuiTypography allows us to name and export our own component as Typography.

We are passing fontWeight property to the useStyles that generates us new overrides class which we can pass to the className property.

<Typography fontWeight={700}>This text is 700 bold</Typography>

Now if you open your browser’s developer tools and select the element, you will notice that it created a new class on top and overrode the original font weight.

https://cdn-images-1.medium.com/max/1600/1*JCVP4OBLLUIJH8HsN-ZNuw.png

But wait… we are losing something here, in fact, a lot — now we are unable to use any of the original Typography props like variant and color.

Let’s bring them back.

For this purpose, we will use a library that will help us to merge multiple classNames very easily. And yes it is called Classnames.

npm install classnames

đź’ˇ There are other great alternatives similar to Classnames in JavaScript like clsx. Or you can always create your own implementation.

import {
makeStyles,
Typography as MuiTypography,
} from '@material-ui/core'
import classNames from 'classnames'
const useStyles = makeStyles({
overrides: {
fontWeight: ({ fontWeight }) => fontWeight,
},
})
export const Typography = ({
fontWeight,
children,
...otherProps
}) => {
const classes = useStyles({ fontWeight })
return (
<MuiTypography
{...otherProps}
className={classNames(
otherProps.className,
classes.overrides,
)}>
{children}
</MuiTypography>
)
}

As you can see we are using spread syntax (…) with otherProps, which allows us to pass all the props except fontWeight and children straight to the original Typography component.

Notice that we also pass otherProps.className to the original className property, because otherwise, we would lose the ability to add className to our new Typography component.

<Typography variant="h1" fontWeight={700} className="anotherClass">
Bold heading
</Typography>

Great! now we can add our custom font-weight alongside the original Material-UI Typography props. And if we don’t pass thefontWeight prop, it keeps its default value from the theme configuration.

At this point, we have achieved our goal and are good to go. But for me, consistency is the key to a great user experience, and we are losing it here as we can use any font-weight available.

Visual consistency is one of the most fundamental principles in web design.

I suggest restricting our component with fewer options so that we can be at least a little more consistent in our project.

import {
makeStyles,
Typography as MuiTypography,
} from '@material-ui/core'
import classNames from 'classnames'
const FONT_WEIGHT = {
light: 300,
regular: 400,
bold: 700,
}
const useStyles = makeStyles({
overrides: {
fontWeight: ({ fontWeight }) =>FONT_WEIGHT[fontWeight],
},
})
export const Typography = ({
fontWeight,
children,
...otherProps
}) => {
const classes = useStyles({ fontWeight })
return (
<MuiTypography
{...otherProps}
className={classNames(
otherProps.className,
classes.overrides,
)}>
{children}
</MuiTypography>
)
}

Now our options are only “light”, “regular” and “bold”.

<Typography variant="h1" fontWeight="light" >
Light weight heading
</Typography>

Adding emphasis property

Changing font weight is great, but that was not the reason why I decided to add custom props in the first place, I really wanted to adjust the opacity of text quickly and easily while keeping it consistent.

Having additionalemphasis property is a straightforward way to control the visual hierarchy of the website, and I love to use it on every project.

Dark text on light backgrounds applies the following opacity levels:

High-emphasis text has an opacity of 87%

Medium-emphasis text and hint text have opacities of 60%

Disabled text has an opacity of 38%

đź’ˇ Read more at material.io

import {
makeStyles,
Typography as MuiTypography,
} from '@material-ui/core'
import classNames from 'classnames'
const FONT_WEIGHT = {
light: 300,
regular: 400,
bold: 700,
}
const EMPHASIS = {
disabled: 0.3,
medium: 0.6,
high: 0.87,
full: 1,
}
const useStyles = makeStyles({
overrides: {
opacity: ({ emphasis }) => EMPHASIS[emphasis],
fontWeight: ({ fontWeight }) => FONT_WEIGHT[fontWeight],
},
})
export const Typography = ({
emphasis = 'high',
fontWeight,
children,
...otherProps
}) => {
const classes = useStyles({ emphasis, fontWeight })
return (
<MuiTypography
{...otherProps}
className={classNames(
otherProps.className,
classes.overrides,
)}>
{children}
</MuiTypography>
)
}

As you can see creatingemphasis prop was similar to fontWeight and the only additional thing we added is the default value — emphasis = 'high', this makes our texts opacity 87% by default which is perfect for most cases. But we still have an option to set it back to 100% emphasis = 'full'.

<Typography variant="h3" fontWeight="light" emphasis="medium" >
We have control!
</Typography>
https://cdn-images-1.medium.com/max/1600/1*hLK0Xkz5VT0oHO_FGm9NkQ.png

To wrap up

There is always freedom to add any custom properties but remember that getting lost in that freedom is easy and things can quickly turn into a mess, especially when the project grows.

Having the least possible amount of options is usually the best, so try to have some restrictions, that will help you keep the consistency and make a better user experience.

Hope that helps!

--

--