Categories
Material UI

Material UI — Customize Tooltips

Material UI is a Material Design library made for React.

It’s a set of React components that have Material Design styles.

In this article, we’ll look at how to customize tooltips with Material UI.

Triggers

We can change how tooltips are triggered.

To disable displaying tooltip on hover, we can use the disableHoverListener prop.

The disableFocusListener prop lets us disable display tooltip when we focus on an element.

disableTouchListener lets us disable tooltip display when we touch an element.

For example, we can write:

import React from "react";
import DeleteIcon from "@material-ui/icons/Delete";
import IconButton from "@material-ui/core/IconButton";
import Tooltip from "@material-ui/core/Tooltip";

export default function App() {
  return (
    <div>
      <Tooltip title="Delete File" disableFocusListener disableTouchListener>
        <IconButton>
          <DeleteIcon />
        </IconButton>
      </Tooltip>
    </div>
  );
}

to disable the focus and touch listeners.

This way, we’ll see the tooltip if we hover or click on it.

We can also control the opening of the tooltip the way we want.

We can just make it show on click and disappears when we click away.

To do that, we write:

import React from "react";
import DeleteIcon from "@material-ui/icons/Delete";
import IconButton from "@material-ui/core/IconButton";
import Tooltip from "@material-ui/core/Tooltip";
import ClickAwayListener from "@material-ui/core/ClickAwayListener";

export default function App() {
  const [open, setOpen] = React.useState(false);

  const handleTooltipClose = () => {
    setOpen(false);
  };

  const handleTooltipOpen = () => {
    setOpen(true);
  };

  return (
    <div>
      <ClickAwayListener onClickAway={handleTooltipClose}>
        <Tooltip
          open={open}
          title="Delete File"
          disableFocusListener
          disableHoverListener
          disableTouchListener
        >
          <IconButton onClick={handleTooltipOpen}>
            <DeleteIcon />
          </IconButton>
        </Tooltip>
      </ClickAwayListener>
    </div>
  );
}

We added a ClickAwayListener to close the tooltip with the handleTooltipClose function when we click away.

And when we click on the IconButton , the handleTooltipOpen will open the tooltip.

These are done by changing the open state.

We pass it straight into the open prop of the Tooltip .

Also, we have disableFocusListener, disableHoverListener , and disableTouchListener to disable all the listeners.

Controlled Tooltips

To make a tooltip a controlled tooltip, we can use the open , onClose and onOpen props.

For example, we can write:

import React from "react";
import DeleteIcon from "@material-ui/icons/Delete";
import IconButton from "@material-ui/core/IconButton";
import Tooltip from "@material-ui/core/Tooltip";

export default function App() {
  const [open, setOpen] = React.useState(false);

  const handleTooltipClose = () => {
    setOpen(false);
  };

  const handleTooltipOpen = () => {
    setOpen(true);
  };

  return (
    <div>
      <Tooltip
        open={open}
        onClose={handleTooltipClose}
        onOpen={handleTooltipOpen}
        title="Delete File"
      >
        <IconButton>
          <DeleteIcon />
        </IconButton>
      </Tooltip>
    </div>
  );
}

to add a Tooltip with the opening and closing of it controlled by the open prop.

The prop is set to the open state.

Then we have the onClose prop to set the tooltip to close when we do something like hover away to close it.

It sets the open state to false .

We also have the onOpen prop to open the tooltip by setting the open state to true .

Variable Width

We can change the width of the tooltip by setting the tooltip class with a custom width.

For example, we can write:

import React from "react";
import Tooltip from "@material-ui/core/Tooltip";
import Button from "@material-ui/core/Button";
import { makeStyles } from "@material-ui/core/styles";

const useStyles = makeStyles(theme => ({
  customWidth: {
    maxWidth: 500
  }
}));

const longText = `
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam sagittis ullamcorper vehicula. Integer viverra est sed purus vulputate tempus. Vestibulum facilisis, metus et sollicitudin cursus, mi sapien suscipit nunc, vitae tristique diam tortor a urna. Proin auctor, ante ac viverra posuere, urna tortor semper mauris,
`;

export default function App() {
  const classes = useStyles();

  return (
    <div>
      <Tooltip title={longText} classes={{ tooltip: classes.customWidth }}>
        <Button className={classes.button}>hover me</Button>
      </Tooltip>
    </div>
  );
}

We add a longText string which we pass into the title prop.

This will be displayed.

classes has the classes for the tooltip.

We set the tooltip property, to set the tooltip class to the customWidth class returned by makeStyles .

Interactive

We can make a tooltip interactive the interactive prop.

This way, it won’t close when the user hovers over the tooltip before the delay to close it expires.

For example, we can write:

import React from "react";
import Button from "@material-ui/core/Button";
import Tooltip from "@material-ui/core/Tooltip";

export default function App() {
  return (
    <Tooltip title="tooltip" interactive>
      <Button>Interactive</Button>
    </Tooltip>
  );
}

This way, we’ll see a delay before it closes when we hover away from the button.

Conclusion

There are many ways to customize tooltips.

We can change how it’s triggered.

Also, we can set leave delays, the width, and make it a controlled component.

Categories
Material UI

Material UI — Customize Tables

Material UI is a Material Design library made for React.

It’s a set of React components that have Material Design styles.

In this article, we’ll look at how to add tables with Material UI.

Customized Tables

We can customize the styling of table components with the withStyles function.

For example, we can write:

import React from "react";
import { withStyles } from "@material-ui/core/styles";
import Table from "@material-ui/core/Table";
import TableBody from "@material-ui/core/TableBody";
import TableCell from "@material-ui/core/TableCell";
import TableContainer from "@material-ui/core/TableContainer";
import TableHead from "@material-ui/core/TableHead";
import TableRow from "@material-ui/core/TableRow";
import Paper from "@material-ui/core/Paper";

const StyledTableCell = withStyles(theme => ({
  head: {
    backgroundColor: theme.palette.common.black,
    color: theme.palette.common.white
  },
  body: {
    fontSize: 12
  }
}))(TableCell);

const StyledTableRow = withStyles(theme => ({
  root: {
    "&:nth-of-type(odd)": {
      backgroundColor: theme.palette.action.hover
    }
  }
}))(TableRow);

function createData(name, age) {
  return { name, age };
}

const rows = [createData("james", 15), createData("mary", 23)];

export default function App() {
  return (
    <TableContainer component={Paper}>
      <Table>
        <TableHead>
          <TableRow>
            <StyledTableCell>name</StyledTableCell>
            <StyledTableCell align="right">age</StyledTableCell>
          </TableRow>
        </TableHead>
        <TableBody>
          {rows.map(row => (
            <StyledTableRow key={row.name}>
              <StyledTableCell component="th" scope="row">
                {row.name}
              </StyledTableCell>
              <StyledTableCell align="right">{row.age}</StyledTableCell>
            </StyledTableRow>
          ))}
        </TableBody>
      </Table>
    </TableContainer>
  );
}

We created our own table cell and table row components.

All we did is pass all the styles into the withStyles function.

Then we put them all in the App component.

Fixed Header

We can make the header fixed with the stickyHeader prop.

For example, we can write:

import React from "react";
import Table from "@material-ui/core/Table";
import TableBody from "@material-ui/core/TableBody";
import TableCell from "@material-ui/core/TableCell";
import TableContainer from "@material-ui/core/TableContainer";
import TableHead from "@material-ui/core/TableHead";
import TableRow from "@material-ui/core/TableRow";
import Paper from "@material-ui/core/Paper";
import { makeStyles } from "@material-ui/core/styles";

const useStyles = makeStyles({
  container: {
    maxHeight: 200
  }
});

function createData(name, age) {
  return { name, age };
}

const rows = [
  createData("james", 15),
  createData("mary", 15),
  createData("alex", 15),
  createData("may", 15),
  createData("sam", 15),
  createData("john", 15),
  createData("mary", 23)
];

export default function App() {
  const classes = useStyles();

  return (
    <TableContainer component={Paper} className={classes.container}>
      <Table stickyHeader>
        <TableHead>
          <TableRow>
            <TableCell>name</TableCell>
            <TableCell align="right">age</TableCell>
          </TableRow>
        </TableHead>
        <TableBody>
          {rows.map(row => (
            <TableRow key={row.name}>
              <TableCell component="th" scope="row">
                {row.name}
              </TableCell>
              <TableCell align="right">{row.age}</TableCell>
            </TableRow>
          ))}
        </TableBody>
      </Table>
    </TableContainer>
  );
}

We have the makeStyles function to style the table with the height.

This way, we get a sticky header.

Then we create the rows for the table.

And then to make the table sticky we have the stickyHeader prop for the Table .

Now when we scroll the content, we’ll see the header stay on top.

Collapsible Table

We can make a table with a collapsible column table with the TableRow component.

Inside it, we add our TableCell with the Collapse component inside it.

To do that, we can write:

import React from "react";
import PropTypes from "prop-types";
import { makeStyles } from "@material-ui/core/styles";
import Box from "@material-ui/core/Box";
import Collapse from "@material-ui/core/Collapse";
import IconButton from "@material-ui/core/IconButton";
import Table from "@material-ui/core/Table";
import TableBody from "@material-ui/core/TableBody";
import TableCell from "@material-ui/core/TableCell";
import TableContainer from "@material-ui/core/TableContainer";
import TableHead from "@material-ui/core/TableHead";
import TableRow from "@material-ui/core/TableRow";
import Typography from "@material-ui/core/Typography";
import Paper from "@material-ui/core/Paper";
import KeyboardArrowDownIcon from "@material-ui/icons/KeyboardArrowDown";
import KeyboardArrowUpIcon from "@material-ui/icons/KeyboardArrowUp";

function createData(name, age, history) {
  return { name, age, history };
}

const rows = [createData("james", 15, [{ date: "2020-01-01" }])];

function Row(props) {
  const { row } = props;
  const [open, setOpen] = React.useState(false);

  return (
    <React.Fragment>
      <TableRow>
        <TableCell>
          <IconButton
            aria-label="expand row"
            size="small"
            onClick={() => setOpen(!open)}
          >
            {open ? <KeyboardArrowUpIcon /> : <KeyboardArrowDownIcon />}
          </IconButton>
        </TableCell>
        <TableCell component="th" scope="row">
          {row.name}
        </TableCell>
        <TableCell>{row.age}</TableCell>
      </TableRow>
      <TableRow>
        <TableCell style={{ paddingBottom: 0, paddingTop: 0 }} colSpan={6}>
          <Collapse in={open} timeout="auto" unmountOnExit>
            <Box margin={1}>
              <Typography variant="h6" gutterBottom component="div">
                History
              </Typography>
              <Table size="small">
                <TableHead>
                  <TableRow>
                    <TableCell>Date</TableCell>
                  </TableRow>
                </TableHead>
                <TableBody>
                  {row.history.map(historyRow => (
                    <TableRow key={historyRow.date}>
                      <TableCell component="th" scope="row">
                        {historyRow.date}
                      </TableCell>
                    </TableRow>
                  ))}
                </TableBody>
              </Table>
            </Box>
          </Collapse>
        </TableCell>
      </TableRow>
    </React.Fragment>
  );
}

export default function App() {
  return (
    <TableContainer component={Paper}>
      <Table>
        <TableHead>
          <TableRow>
            <TableCell />
            <TableCell>name</TableCell>
            <TableCell>age</TableCell>
          </TableRow>
        </TableHead>
        <TableBody>
          {rows.map(row => (
            <Row key={row.name} row={row} />
          ))}
        </TableBody>
      </Table>
    </TableContainer>
  );
}

We created a Row component and then TableCell .

Inside it, we have the Collapse component that we can click to toggle a table on or off.

And inside that, we have a Table with more stuff inside it.

Conclusion

We can add table components with customized styles.

Also, we have can make a table with another table nested inside that we can expand or collapse.

Categories
Material UI

Material UI — Chips and Dividers

Material UI is a Material Design library made for React.

It’s a set of React components that have Material Design styles.

In this article, we’ll look at how to customize chips and add dividers with Material UI.

Small Chip

We can add the size prop to change the size of the chip.

For example, we can write:

import React from "react";
import Chip from "@material-ui/core/Chip";

export default function App() {
  return (
    <div>
      <Chip size="small" label="Basic" />
    </div>
  );
}

to make a small basic chip.

Outlined Variant

We can make the chip outlined by setting the variant prop to outlined .

For example, we can write:

import React from "react";
import Chip from "@material-ui/core/Chip";

export default function App() {
  return (
    <div>
      <Chip variant="outlined" label="Basic" />
    </div>
  );
}

Avatars

We can add avatars to chips.

For example, we can write:

import React from "react";
import Chip from "@material-ui/core/Chip";
import Avatar from "@material-ui/core/Avatar";

export default function App() {
  return (
    <div>
      <Chip
        label="cat"
        avatar={<Avatar alt="cat" src="[http://placekitten.com/200/200](http://placekitten.com/200/200)" />}
      />
    </div>
  );
}

We set the label to display with the label prop.

avatar has the avatar we want to show on the left.

Icons

We can display an icon besides the chip label.

To do that, we set an icon component as the value of the icon prop.

For example, we can write:

import React from "react";
import Chip from "@material-ui/core/Chip";
import FaceIcon from "@material-ui/icons/Face";

export default function App() {
  return (
    <div>
      <Chip label="person" icon={<FaceIcon />} />
    </div>
  );
}

We set the icon to the FaceIcon to display it to the left of the label.

The delete icon can be changed with the deleteIcon label.

For example, we can write:

import React from "react";
import Chip from "@material-ui/core/Chip";
import DoneIcon from "@material-ui/icons/Done";

export default function App() {
  const handleDelete = () => {
    console.info("delete icon clicked");
  };

  return (
    <div>
      <Chip label="task" onDelete={handleDelete} deleteIcon={<DoneIcon />} />
    </div>
  );
}

to add a delete icon of our choice.

We have the onDelete prop to run a function to do something when it’s clicked.

Also, we have the deleteIcon to show the delete icon that we set.

We can also make a chip clickable by passing in an onClick prop.

For example, we can write:

import React from "react";
import Chip from "@material-ui/core/Chip";

export default function App() {
  const handleClick = () => {
    console.info("d clicked");
  };

  return (
    <div>
      <Chip label="task" onClick={handleClick} />
    </div>
  );
}

We add the onClick prop and pass in a function to it to handle clicks.

Divider

Dividers lets us add a thin line to group items.

We can add dividers to lists.

For example, we can write:

import React from "react";
import List from "@material-ui/core/List";
import ListItem from "@material-ui/core/ListItem";
import ListItemText from "@material-ui/core/ListItemText";
import Divider from "@material-ui/core/Divider";

const fruits = [
  { key: 1, name: "apple" },
  { key: 2, name: "orange" },
  { key: 3, name: "grape" }
];

export default function App() {
  return (
    <div>
      <List component="nav">
        {fruits.map(f => [
          <ListItem button>
            <ListItemText primary={f.name} key={f.key} />
          </ListItem>,
          <Divider />
        ])}
      </List>
    </div>
  );
}

to add a divider below each ListItem .

Inset Dividers

We can add dividers that don’t take up the full width of the container.

For example, we can write:

import React from "react";
import List from "@material-ui/core/List";
import ListItem from "@material-ui/core/ListItem";
import ListItemText from "@material-ui/core/ListItemText";
import Divider from "@material-ui/core/Divider";
import ListItemAvatar from "@material-ui/core/ListItemAvatar";
import Avatar from "@material-ui/core/Avatar";
import ImageIcon from "@material-ui/icons/Image";

const fruits = [
  { key: 1, name: "apple" },
  { key: 2, name: "orange" },
  { key: 3, name: "grape" }
];

export default function App() {
  return (
    <div>
      <List component="nav">
        {fruits.map(f => [
          <ListItem>
            <ListItemAvatar>
              <Avatar>
                <ImageIcon />
              </Avatar>
            </ListItemAvatar>
            <ListItemText primary={f.name} key={f.key} secondary={f.name} />
          </ListItem>,
          <Divider variant="inset" component="li" />
        ])}
      </List>
    </div>
  );
}

to add the list items with an avatar.

To the right of it, we have the list item text.

Then after that, we have the Divider component.

The variant is set to inset to make it display below the text only.

component is set to li to display it as an li element.

Conclusion

We can add chips to display small pieces of text, images, and icons.

Also, we can show dividers to separate items.

Categories
Material UI

Material UI — Tabs

Material UI is a Material Design library made for React.

It’s a set of React components that have Material Design styles.

In this article, we’ll look at how to add tabs with Material UI.

Tabs

We can add tabs with the Tabs component.

For example, we can write:

import React from "react";
import AppBar from "@material-ui/core/AppBar";
import Tabs from "@material-ui/core/Tabs";
import Tab from "@material-ui/core/Tab";
import Box from "@material-ui/core/Box";

function TabPanel(props) {
  const { children, value, index, ...other } = props;

  return (
    <div {...other}>
      {value === index && <Box p={3}>{children}</Box>}
    </div>
  );
}

export default function App() {
  const [value, setValue] = React.useState(0);

  const handleChange = (event, newValue) => {
    setValue(newValue);
  };

  return (
    <>
      <AppBar position="static">
        <Tabs value={value} onChange={handleChange}>
          <Tab label="Item One" />
          <Tab label="Item Two" />
          <Tab label="Item Three" />
        </Tabs>
      </AppBar>
      <TabPanel value={value} index={0}>
        Item One
      </TabPanel>
      <TabPanel value={value} index={1}>
        Item Two
      </TabPanel>
      <TabPanel value={value} index={2}>
        Item Three
      </TabPanel>
    </>
  );
}

We create our own TabPanel to show the content that belongs to the given tab.

We pass in the value to the value prop and compare that with the index prop to check what to display.

In the TabPanel component, we compare value and index to see if they’re the same.

If they’re, then we display the content.

Otherwise, we hide it.

label has the text for the tab.

We’ll update the value with the newValue , which is the index of the tab.

Wrapped Labels

If we have long labels, then it may be cut off if it’s too long.

To make it wrap, we can add the wrapped prop.

For example, we can write:

import React from "react";
import AppBar from "@material-ui/core/AppBar";
import Tabs from "@material-ui/core/Tabs";
import Tab from "@material-ui/core/Tab";
import Box from "@material-ui/core/Box";

function TabPanel(props) {
  const { children, value, index, ...other } = props;

  return <div {...other}>{value === index && <Box p={3}>{children}</Box>}</div>;
}

export default function App() {
  const [value, setValue] = React.useState(0);

  const handleChange = (event, newValue) => {
    setValue(newValue);
  };

  return (
    <>
      <AppBar position="static">
        <Tabs value={value} onChange={handleChange}>
          <Tab
            label="Lorem ipsum dolor sit amet, consectetur adipiscing elit."
            wrapped
          />
          <Tab label="Item Two" />
          <Tab label="Item Three" />
        </Tabs>
      </AppBar>
      <TabPanel value={value} index={0}>
        Item One
      </TabPanel>
      <TabPanel value={value} index={1}>
        Item Two
      </TabPanel>
      <TabPanel value={value} index={2}>
        Item Three
      </TabPanel>
    </>
  );
}

The first Tab has a long label value.

Since we added the wrapped prop, it’ll be wrapped.

Disabled Tab

To disable a tab, we can add the disabled prop.

For instance, we can write:

import React from "react";
import AppBar from "@material-ui/core/AppBar";
import Tabs from "@material-ui/core/Tabs";
import Tab from "@material-ui/core/Tab";
import Box from "@material-ui/core/Box";

function TabPanel(props) {
  const { children, value, index, ...other } = props;

  return <div {...other}>{value === index && <Box p={3}>{children}</Box>}</div>;
}

export default function App() {
  const [value, setValue] = React.useState(0);

  const handleChange = (event, newValue) => {
    setValue(newValue);
  };

  return (
    <>
      <AppBar position="static">
        <Tabs value={value} onChange={handleChange}>
          <Tab label="Item One" />
          <Tab label="Item Two" />
          <Tab label="Item Three" disabled />
        </Tabs>
      </AppBar>
      <TabPanel value={value} index={0}>
        Item One
      </TabPanel>
      <TabPanel value={value} index={1}>
        Item Two
      </TabPanel>
      <TabPanel value={value} index={2}>
        Item Three
      </TabPanel>
    </>
  );
}

Then we can disable the 3rd tab.

Full-Width Tabs

We can make tabs full width with the variant prop set to fullWidth .

To do that, we write:

import React from "react";
import AppBar from "@material-ui/core/AppBar";
import Tabs from "@material-ui/core/Tabs";
import Tab from "@material-ui/core/Tab";
import Box from "@material-ui/core/Box";

function TabPanel(props) {
  const { children, value, index, ...other } = props;

  return <div {...other}>{value === index && <Box p={3}>{children}</Box>}</div>;
}

export default function App() {
  const [value, setValue] = React.useState(0);

  const handleChange = (event, newValue) => {
    setValue(newValue);
  };

  return (
    <>
      <AppBar position="static">
        <Tabs value={value} onChange={handleChange} variant="fullWidth">
          <Tab label="Item One" />
          <Tab label="Item Two" />
        </Tabs>
      </AppBar>
      <TabPanel value={value} index={0}>
        Item One
      </TabPanel>
      <TabPanel value={value} index={1}>
        Item Two
      </TabPanel>
    </>
  );
}

to make all tabs fill the space evenly.

Together, they take up the full width of the screen.

Conclusion

We can add tabs with the Tabs component.

The content can be added to any element.

We can determine what to display by check the index of our content against the value that we set when clicking the tabs.

Categories
Material UI

Material UI — Steppers

Material UI is a Material Design library made for React.

It’s a set of React components that have Material Design styles.

In this article, we’ll look at how to add steppers with Material UI.

Stepper

A stepper lets us show the steps that a user has to go through to complete a process.

To use it, we can use the Stepper component add the stepper:

import React from "react";
import Stepper from "@material-ui/core/Stepper";
import Step from "@material-ui/core/Step";
import StepLabel from "@material-ui/core/StepLabel";
import Button from "@material-ui/core/Button";

function getSteps() {
  return ["step 1", "step 2", "step 3"];
}

function getStepContent(step) {
  switch (step) {
    case 0:
      return "do step 1";
    case 1:
      return "do step 2";
    case 2:
      return "do steo 3";
    default:
      return "unknown step";
  }
}

export default function App() {
  const [activeStep, setActiveStep] = React.useState(0);
  const [skipped, setSkipped] = React.useState(new Set());
  const steps = getSteps();

  const isStepOptional = step => {
    return step === 1;
  };

  const isStepSkipped = step => {
    return skipped.has(step);
  };

  const handleNext = () => {
    let newSkipped = skipped;
    if (isStepSkipped(activeStep)) {
      newSkipped = new Set(newSkipped.values());
      newSkipped.delete(activeStep);
    }

  setActiveStep(prevActiveStep => prevActiveStep + 1);
    setSkipped(newSkipped);
  };

  const handleBack = () => {
    setActiveStep(prevActiveStep => prevActiveStep - 1);
  };

  const handleSkip = () => {
    if (!isStepOptional(activeStep)) {
      throw new Error("You can't skip a step that isn't optional.");
    }

  setActiveStep(prevActiveStep => prevActiveStep + 1);
    setSkipped(prevSkipped => {
      const newSkipped = new Set(prevSkipped.values());
      newSkipped.add(activeStep);
      return newSkipped;
    });
  };

  const handleReset = () => {
    setActiveStep(0);
  };

  return (
    <div>
      <Stepper activeStep={activeStep}>
        {steps.map((label, index) => {
          const stepProps = {};
          const labelProps = {};
          if (isStepOptional(index)) {
            labelProps.optional = "optional";
          }
          if (isStepSkipped(index)) {
            stepProps.completed = false;
          }
          return (
            <Step key={label} {...stepProps}>
              <StepLabel {...labelProps}>{label}</StepLabel>
            </Step>
          );
        })}
      </Stepper>
      <div>
        {activeStep === steps.length ? (
          <div>
            All steps completed
            <Button onClick={handleReset}>Reset</Button>
          </div>
        ) : (
          <div>
            {getStepContent(activeStep)}
            <div>
              <Button disabled={activeStep === 0} onClick={handleBack}>
                Back
              </Button>
              {isStepOptional(activeStep) && (
                <Button
                  variant="contained"
                  color="primary"
                  onClick={handleSkip}
                >
                  Skip
                </Button>
              )}

              <Button variant="contained" color="primary" onClick={handleNext}>
                {activeStep === steps.length - 1 ? "Finish" : "Next"}
              </Button>
            </div>
          </div>
        )}
      </div>
    </div>
  );
}

We create the step headings with the getSteps function.

The content of each step is in the getStepContent function.

When we click the Next button, handleNext is called.

Then we add the steps that are added to the skipped state.

handleBack is run when the back button is clicked.

We just set the activeStep to the previous step.

The handleSkip function is called when the skip button is clicked.

We check if an active step is optional.

If it’s optional, then we let them skip it and add it to the skipped set.

In the JSX code, we use the Stepper component with the Step prop to add the steps.

Then we add the content for the step and the buttons below it.

The content is shown according to the activeStep value.

Alternative Label

We can change the step labels to what we want with the StepLabel component.

For instance, we can write:

import React from "react";
import Stepper from "@material-ui/core/Stepper";
import Step from "@material-ui/core/Step";
import StepLabel from "@material-ui/core/StepLabel";
import Button from "@material-ui/core/Button";

function getSteps() {
  return ["step 1", "step 2", "step 3"];
}

function getStepContent(step) {
  switch (step) {
    case 0:
      return "do step 1";
    case 1:
      return "do step 2";
    case 2:
      return "do steo 3";
    default:
      return "unknown step";
  }
}

export default function App() {
  const [activeStep, setActiveStep] = React.useState(0);
  const steps = getSteps();

  const handleNext = () => {
    setActiveStep(prevActiveStep => prevActiveStep + 1);
  };

  const handleBack = () => {
    setActiveStep(prevActiveStep => prevActiveStep - 1);
  };

  const handleReset = () => {
    setActiveStep(0);
  };

  return (
    <div>
      <Stepper activeStep={activeStep} alternativeLabel>
        {steps.map(label => (
          <Step key={label}>
            <StepLabel>{label}</StepLabel>
          </Step>
        ))}
      </Stepper>
      <div>
        {activeStep === steps.length ? (
          <div>
            All steps completed
            <Button onClick={handleReset}>Reset</Button>
          </div>
        ) : (
          <div>
            {getStepContent(activeStep)}

<div>
              <Button disabled={activeStep === 0} onClick={handleBack}>
                Back
              </Button>
              <Button variant="contained" color="primary" onClick={handleNext}>
                {activeStep === steps.length - 1 ? "Finish" : "Next"}
              </Button>
            </div>
          </div>
        )}
      </div>
    </div>
  );
}

We added the alternativeLabel prop to let us override the label.

Then in the render children prop, we return our own Step component with the StepLabel to display the step number.

Conclusion

We can add a stepper with the Stepper component.

We can make steps optional and customize the labels.

The content can be displayed below it.