Search code examples
reactjsmaterial-ui

How to make a loading button in Material UI?


I am using with in Material UI framework and I was wondering: How can I create a loading button using this framework?

I am looking for something similar to this.


Solution

  • To the best of my knowledge, there is no single component that accomplishes this out of the box in material-ui. However, you can implement your own easily using CircularProgress.

    Assuming you are using material-ui v1, here's a rough example. First, I create a LoadingButton that accepts a loading prop - if that prop is true, I display a CircularProgress indicator. It also accepts a done prop - if that's true, the button clears the progress indicator and becomes a checkmark to show success.

    import React from 'react';
    import PropTypes from 'prop-types';
    import { withStyles } from 'material-ui/styles';
    import Button from 'material-ui/Button';
    import { CircularProgress } from 'material-ui/Progress';
    import Check from 'material-ui-icons/Check';
    
    const styles = theme => ({
      button: {
        margin: theme.spacing.unit,
      },
    });
    
    const LoadingButton = (props) => {
      const { classes, loading, done, ...other } = props;
    
      if (done) {
        return (
          <Button className={classes.button} {...other} disabled>
            <Check />
          </Button>
        );
      }
      else if (loading) {
        return (
          <Button className={classes.button} {...other}>
            <CircularProgress />
          </Button>
        );
      } else {
        return (
          <Button className={classes.button} {...other} />
        );
      }
    }
    
    LoadingButton.defaultProps = {
      loading: false,
      done: false,
      };
    
    LoadingButton.propTypes = {
      classes: PropTypes.object.isRequired,
      loading: PropTypes.bool,
      done: PropTypes.bool,
    };
    
    export default withStyles(styles)(LoadingButton);
    

    You can use the LoadingButton as shown in the following example, which uses state to set the appropriate prop on the button.

    import React from 'react';
    import LoadingButton from './LoadingButton';
    
    class ControlledButton extends React.Component {
      constructor(props) {
        super(props);
    
        this.state = { loading: false, finished: false };
      }
    
      render() {
        const { loading, finished } = this.state;
    
        const setLoading = !finished && loading;
    
        return (
          <div>
            <LoadingButton
              loading={setLoading}
              done={finished}
              onClick={() => {
                // Clicked, so show the progress dialog
                this.setState({ loading: true });
    
                // In a 1.5 seconds, end the progress to show that it's done
                setTimeout(() => { this.setState({ finished: true })}, 1500);
              }}
            >
              Click Me
            </LoadingButton>
          </div>
        );
      }
    }
    
    export default ControlledButton;
    

    You can of course tweak the styling and functionality to meet your exact needs.