The Material-UI Skeleton gives a great visual treat to users of your website or app. It’s strength is that it makes the user feel as though the app is useful and useable before critical loading is completed.
Let’s configure a Material-UI Skeleton Loader to display for 1 second OR the loading time of an API call, whichever is greater.

Typically users don’t need a loading icon if load time is less than 1 second, but if you are uncertain how long an API may take to respond, you may want to avoid flashing a component on the screen before showing a loading indicator.
Alternatively, you could show nothing at all for one second before choosing to show a loading indicator. Or you could show the indicator immediately, which is what I will show in this demo. Let’s get to coding!
The finished Code Sandbox is in the Resources section. We’ll walk through the code step-by-step below.
Skeleton Code
We’ll start with these constants:
const [minimumTime, setMinimumTime] = useState(500);
const [minimumTimeElapsed, setMinimumTimeElapsed] = useState(true);
const [loading, setLoading] = useState(false);
If this weren’t a demo, you would have a const minimumTime = 500;
(or whatever desired value) instead of the state const above. In this demo, the minimum time is adjustable by the user.
minimumTimeElapsed is one of the two checks performed by the logic to determine if the <Skeleton /> should be shown. loading is the other variable checked, and it is true or false depending on if our simulated API call has completed.
Below is the ternary statement in our JSX that uses the two constants mentioned above. It either shows the <Skeleton/> component or the <JobTable/> component if the user-specified minimum visible time has not elapsed or the simulated loading has not finished.
{
!minimumTimeElapsed || loading
? <Skeleton style={{ height: 200 }} />
: <JobTable />
}
Notice how I set the Skeleton height. I played around with trying to dynamically determine the height, but it was difficult to match the Skeleton and table in a reliable way.
Given that our hypothetical user requirements state that it should be visible if a) a minimum time has not elapsed, or b) the API call has not completed yet, we need a way to determine when to flip the state. The state is time based, so we will have to use setTimeout within a callback:
const restartTimeout = useCallback(() => {
setMinimumTimeElapsed(false);
setTimeout(() => {
setMinimumTimeElapsed(true);
}, minimumTime);
//simulate random load time between 0 and 5 seconds
const randomLoadTime = Math.random() * 5000;
setLoading(true);
//simulate loading
setTimeout(() => {
setLoading(false);
}, randomLoadTime);
}, [setMinimumTimeElapsed, setLoading, minimumTime]);
The restartTimeout function takes a user-specified minimum time and flips minimumTimeElapsed
to true when the setTimeout callback is called.
The setTimeout that flips loading
to false is only necessary for demo purposes. In a real situation, your code would instead likely be observing a loading
observable in a store.
Finally, the JSX where we bring the Skeleton together with TextField, Button, and Table components:
<div className="App">
<h2>Skeleton Loader with Minimum Time</h2>
<div style={{ maxWidth: "100%", paddingTop: 12 }}>
<TextField
variant={"outlined"}
style={{ margin: 8, minWidth: 250 }}
label={"Set minimum wait"}
onChange={(e) => setMinimumTime(e.target.value)}
/>
<Button
variant="outlined"
style={{ margin: 8, minWidth: 250 }}
onClick={() => {
restartTimeout();
}}
>
Restart Skeleton Timer
</Button>
{!minimumTimeElapsed || loading ? (
<Skeleton style={{ height: 200 }} />
) : (
<JobTable />
)}
</div>
</div>
The Result
Our <Skeleton /> now shows for at least half a second or the loading time of the simulated API call, whichever is longer. Our constants are clearly named so the logic is conveyed to future devs looking at this code.
If you can confirm that the logic is correct and the code is clean, consider your work a job well done.
Consider using the Skeleton component to give users a better experience while waiting for tables and datagrids to load.
Resources
Test your JavaScript knowledge with these 50 challenging interview questions!