The MUI Progress Bar, more formally known as the MUI LinearProgress component, gives a user visual feedback while waiting for resources to load in a web page. In this tutorial I will customize the progress bar with dynamic color based on load value, a percent label, and both vertical and horizontal orientation.
Here’s a screenshot of the linear progress components we will create:

The Resources section contains full working code and a link to a live Code Sandbox demo.
Change MUI Progress Bar Color
We’ll use useEffect
and useState
to dynamically update the color of the LinearProgress component based on it’s value
prop. We’ll use a timer inside useEffect
to set the state value progress
(just like this MUI docs tutorial), but then we’ll also set a color
state value for updating the progress bar’s sx
prop with a color value.
React.useEffect(() => {
if (progress <= 25) {
setColor("255,0,0");
} else if (progress <= 50) {
setColor("255,255,0");
} else if (progress <= 75) {
setColor("0,0,255");
} else {
setColor("0,255,0");
}
const timer = setInterval(() => {
setProgress((previousProgress) => {
if (previousProgress === 100) {
return 0;
}
return previousProgress + 5;
});
return () => {
clearInterval(timer);
};
}, [progress]);
//JSX
<LinearProgress
variant="determinate"
value={progress}
sx={{
backgroundColor: `rgb(${color},0.4)`,
"& .MuiLinearProgress-bar": {
backgroundColor: `rgb(${color})`
}
}}
/>
Notice in the JSX I used sx
instead of the built-in progress bar color
prop. The limitation of the color
prop is that it can only accept colors directly from the theme palette.
Because of this limitation, I chose to directly set the background color for the progress bar so I had unlimited color options. However, this meant I also had to set the background color for the child span with class MuiLinearProgress-bar
. The child element actually contains the darker color bar, while the parent is the full-width and more translucent bar.
The above code is specifically for the determinate
variant. The buffer
variant also accepts a value prop, but then renders with two child spans so you need a different selector to customize the color.
Add Percent to MUI Progress Bar
I added a FormLabel with a percentage value above the progress bar. This label obtains it’s value from the progress
state value that is regularly being updated through a timer in a useEffect
hook.
//state value
const [progress, setProgress] = React.useState(0);
//JSX
<FormLabel>{progress}%</FormLabel>
Create a Vertical MUI Progress Bar
The Material-UI Linear Progress component can be oriented vertically through CSS customization of the transform
property. I will discuss two different methods to create a vertical progress bar below.
Invert Width, Height, and translateY
The first method expands the height, shrinks the width, and translates the y value of the child span (the darker line).
First, we calculate a state variable that counts down from 100%, then we create the JSX and nested selector:
//state value
const [invertedProgress, setInvertedProgress] = React.useState(100);
//inside useEffect hook
setInvertedProgress((previousInvertedProgress) => {
if (previousInvertedProgress === 0) {
return 100;
}
return previousInvertedProgress - 5;
});
//JSX
<LinearProgress
sx={{
width: 4,
height: 200,
"& span.MuiLinearProgress-bar": {
transform: `translateY(-${invertedProgress}%) !important` //has to have !important
}
}}
value={invertedProgress}
/>
The JSX is the tricky part. My progress bar “moves” from top to bottom, so that means I need to count down from 100. Also, I need a negative value for the transform: translateY
.
Notice also that I used !important
. I never have to do that unless MUI is “behind the scenes” using inline styling instead of classes. Sure enough, look at the DOM screenshot below. I had to override that transform: translateX
value in the style attribute with my translateY
value.

Rotate Progress Bar 90 Degrees
Perhaps the easier method conceptually is rotating the bar 90 degrees with transform: rotate(90deg)
. However, it’s major drawback is that it pivots around its central point and requires some micromanagement to place it properly.
<LinearProgress
variant="determinate"
sx={{
transform: "rotate(90deg)",
width: 200,
position: "fixed",
top: 188,
left: -50
}}
value={progress}
/>
In this case I used position: fixed
, top
, and left
to position the linear progress bar.
Resources
In this example I oriented the MUI chip vertically.
The MUI Skeleton Loader is another choice for visual feedback while resources are loading.
Here’s a custom styled MUI CircularProgress indicator.
Full Code:
import * as React from "react";
import Stack from "@mui/material/Stack";
import FormLabel from "@mui/material/FormLabel";
import LinearProgress from "@mui/material/LinearProgress";
export default function LinearDeterminate() {
const [progress, setProgress] = React.useState(0);
const [invertedProgress, setInvertedProgress] = React.useState(100);
const [color, setColor] = React.useState("red");
React.useEffect(() => {
if (progress <= 25) {
setColor("255,0,0");
} else if (progress <= 50) {
setColor("255,255,0");
} else if (progress <= 75) {
setColor("0,0,255");
} else {
setColor("0,255,0");
}
const timer = setInterval(() => {
setProgress((previousProgress) => {
if (previousProgress === 100) {
return 0;
}
return previousProgress + 5;
});
setInvertedProgress((previousInvertedProgress) => {
if (previousInvertedProgress === 0) {
return 100;
}
return previousInvertedProgress - 5;
});
}, 750);
return () => {
clearInterval(timer);
};
}, [progress]);
return (
<Stack sx={{ width: "100%" }}>
<FormLabel>{progress}%</FormLabel>
<LinearProgress
variant="determinate"
value={progress}
sx={{
backgroundColor: `rgb(${color},0.4)`,
"& .MuiLinearProgress-bar": {
backgroundColor: `rgb(${color})`
}
}}
/>
<FormLabel sx={{ marginTop: 4 }}>Vertical Progress</FormLabel>
<LinearProgress
sx={{
width: 4,
height: 200,
"& span.MuiLinearProgress-bar": {
transform: `translateY(-${invertedProgress}%) !important` //has to have !important
}
}}
variant="determinate"
value={invertedProgress}
/>
<LinearProgress
sx={{
transform: "rotate(90deg)",
width: 200,
position: "fixed",
top: 190,
left: -50
}}
variant="determinate"
value={progress}
/>
</Stack>
);
}
Awesome! this helped me! thanks.
I’m glad to hear it!