Tables In Material UI V5 - SaasEasy Blog

Introduction

As a web developer, I often find myself struggling to create functional and visually appealing tables for my web applications. When I discovered Material UI V5, I was intrigued by the promise of a streamlined and customizable table component. In this article, I’ll delve into the world of Material UI V5 Tables and share my insights, tips, and tricks for getting the most out of this powerful tool.

Let’s start with the basics. To get started with Material UI V5 Tables, you’ll need to install and set up the Material UI library. This can be done using npm, yarn, or a direct download. Once you have Material UI set up, you’re ready to create your first table.

import { Table, TableBody, TableCell, TableContainer, 
TableHead, TableRow, Paper } from '@mui/material';

function BasicTable() {
    return (
        <TableContainer component={Paper}>
            <Table>
                <TableHead>
                    <TableRow>
                        <TableCell>First Name</TableCell>
                        <TableCell>Last Name</TableCell>
                        <TableCell>Email</TableCell>
                    </TableRow>
                </TableHead>
                <TableBody>
                    <TableRow>
                        <TableCell>John</TableCell>
                        <TableCell>Doe</TableCell>
                        <TableCell>[email protected]</TableCell>
                    </TableRow>
                    <TableRow>
                        <TableCell>Jane</TableCell>
                        <TableCell>Doe</TableCell>
                        <TableCell>[email protected]</TableCell>
                    </TableRow>
                </TableBody>
            </Table>
        </TableContainer>
    );
}

Here we have a basic table component with two rows of data, each containing a first name, last name and email address. The TableContainer component is used to provide the paper background for the table, while the TableHead and TableBody components provide the structure for the headers and data respectively. The TableCell component is used to display individual cells within the table, while the TableRow component is used for grouping cells into rows.

Of course, this is just a starting point. Material UI V5 Tables are highly customizable, and there are a number of ways to style and enhance the default table. Let’s explore some of these options in more detail.

Customizing Material UI V5 Tables

Styling the Table

One of the great advantages of using Material UI V5 Tables is the ease with which you can style them to fit seamlessly into your application’s design. Here’s an example of how to add some custom styling to our previous table:

import { makeStyles } from '@mui/styles';

const useStyles = makeStyles({
    table: {
        minWidth: 650,
        '& th': {
            fontWeight: 'bold',
            backgroundColor: '#f2f2f2',
            color: '#090909'
        },
        '& tbody tr:nth-of-type(even)': {
            backgroundColor: '#f2f2f2'
        }
    },
});

function StyledTable() {
    const classes = useStyles();

    return (
        <TableContainer component={Paper}>
            <Table className={classes.table}>
                <TableHead>
                    <TableRow>
                        <TableCell>First Name</TableCell>
                        <TableCell>Last Name</TableCell>
                        <TableCell>Email</TableCell>
                    </TableRow>
                </TableHead>
                <TableBody>
                    <TableRow>
                        <TableCell>John</TableCell>
                        <TableCell>Doe</TableCell>
                        <TableCell>[email protected]</TableCell>
                    </TableRow>
                    <TableRow>
                        <TableCell>Jane</TableCell>
                        <TableCell>Doe</TableCell>
                        <TableCell>[email protected]</TableCell>
                    </TableRow>
                </TableBody>
            </Table>
        </TableContainer>
    );
}

Notice the makeStyles hook and the associated table style object. We then add the class classes.table to the Table component to apply our custom styling. In this example, we’ve set a minimum width for the table, added a background color to the header row, and alternating background colors to rows to improve readability.

Adding Pagination

Another useful feature of Material UI V5 Tables is the built-in ability to paginate data. Here’s how to add pagination to our previous example:

import { useState } from 'react';
import { TablePagination } from '@mui/material';

function PaginatedTable() {
    const [page, setPage] = useState(0);
    const [rowsPerPage, setRowsPerPage] = useState(5);

    const handleChangePage = (event, newPage) => {
        setPage(newPage);
    };

    const handleChangeRowsPerPage = (event) => {
        setRowsPerPage(parseInt(event.target.value, 10));
        setPage(0);
    };

    return (
        <div>
            <TableContainer component={Paper}>
                <Table className={classes.table}>
                    <TableHead>
                        <TableRow>
                            <TableCell>First Name</TableCell>
                            <TableCell>Last Name</TableCell>
                            <TableCell>Email</TableCell>
                        </TableRow>
                    </TableHead>
                    <TableBody>
                        {data.slice(page * rowsPerPage, page * rowsPerPage + rowsPerPage).map((row) => (
                            <TableRow key={row.id}>
                                <TableCell>{row.firstName}</TableCell>
                                <TableCell>{row.lastName}</TableCell>
                                <TableCell>{row.email}</TableCell>
                            </TableRow>
                        ))}
                    </TableBody>
                </Table>
            </TableContainer>
            <TablePagination
                rowsPerPageOptions={[5, 10, 25]}
                component="div"
                count={data.length}
                rowsPerPage={rowsPerPage}
                page={page}
                onPageChange={handleChangePage}
                onRowsPerPageChange={handleChangeRowsPerPage}
            />
        </div>
    );
}

Here we’ve added a TablePagination component below the table to control the number of rows displayed per page and handle pagination. Notice the useState hook used to keep track of the current page and number of rows per page. The handleChangePage and handleChangeRowsPerPage functions are called when the user interacts with the pagination controls. We’ve also changed the TableBody component to display a slice of data based on the current page and number of rows per page.

Filtering and Sorting the Table

Filtering and sorting data is another powerful feature provided by Material UI V5 Tables. Here’s an example of how to add filtering and sorting to our previous example:

import { useState } from 'react';
import { TableHead, TableRow, TableSortLabel } from '@mui/material';

const createData = (id, firstName, lastName, email) => {
    return { id, firstName, lastName, email };
};

const rows = [
    createData(1, 'John', 'Doe', '[email protected]'),
    createData(2, 'Jane', 'Doe', '[email protected]'),
    createData(3, 'Bruce', 'Wayne', '[email protected]'),
    createData(4, 'Tony', 'Stark', '[email protected]'),
    createData(5, 'Diana', 'Prince', '[email protected]'),
    createData(6, 'Clark', 'Kent', '[email protected]')
];

function EnhancedTable() {
    const [order, setOrder] = useState('asc');
    const [orderBy, setOrderBy] = useState('firstName');

    const handleRequestSort = (event, property) => {
        const isAsc = orderBy === property && order === 'asc';
        setOrder(isAsc ? 'desc' : 'asc');
        setOrderBy(property);
    };

    return (
        <TableContainer component={Paper}>
            <Table className={classes.table}>
                <TableHead>
                    <TableRow>
                        <TableCell>
                            <TableSortLabel
                                active={orderBy === 'firstName'}
                                direction={orderBy === 'firstName' ? order : 'asc'}
                                onClick={(event) => handleRequestSort(event, 'firstName')}
                            >
                                First Name
                            </TableSortLabel>
                        </TableCell>
                        <TableCell>
                            <TableSortLabel
                                active={orderBy === 'lastName'}
                                direction={orderBy === 'lastName' ? order : 'asc'}
                                onClick={(event) => handleRequestSort(event, 'lastName')}
                            >
                                Last Name
                            </TableSortLabel>
                        </TableCell>
                        <TableCell>
                            <TableSortLabel
                                active={orderBy === 'email'}
                                direction={orderBy === 'email' ? order : 'asc'}
                                onClick={(event) => handleRequestSort(event, 'email')}
                            >
                                Email
                            </TableSortLabel>
                        </TableCell>
                    </TableRow>
                </TableHead>
                <TableBody>
                    {stableSort(rows, getComparator(order, orderBy)).map((row) => (
                        <TableRow key={row.id}>
                            <TableCell>{row.firstName}</TableCell>
                            <TableCell>{row.lastName}</TableCell>
                            <TableCell>{row.email}</TableCell>
                        </TableRow>
                    ))}
                </TableBody>
            </Table>
        </TableContainer>
    );
}

We’ve added a createData function to create our data rows, and a stableSort function to sort the data. The handleRequestSort function is called when a user clicks on a TableSortLabel component to sort the data by the clicked column. Notice how we use the useState hooks to maintain the current sort order and sorted column.

Advanced Functionality of Material UI V5 Tables

Handling Click Events

Material UI V5 Tables provide a way to handle click events for rows, cells and headers. Here’s an example of how to handle a click event for a row:

import { TableRow } from '@mui/material';

function ClickableRowTable() {
    const handleClick = (event, id) => {
        console.log(`Row with id ${id} clicked!`);
    };

    return (
        <TableContainer component={Paper}>
            <Table className={classes.table}>
                <TableHead>
                    <TableRow>
                        <TableCell>First Name</TableCell>
                        <TableCell>Last Name</TableCell>
                        <TableCell>Email</TableCell>
                    </TableRow>
                </TableHead>
                <TableBody>
                    {data.map((row) => (
                        <TableRow key={row.id} hover onClick={(event) => handleClick(event, row.id)}>
                            <TableCell>{row.firstName}</TableCell>
                            <TableCell>{row.lastName}</TableCell>
                            <TableCell>{row.email}</TableCell>
                        </TableRow>
                    ))}
                </TableBody>
            </Table>
        </TableContainer>
    );
}

Here we’ve added a handleClick function that logs a message containing the id of the clicked row. We’ve also added the hover prop to the TableRow component to highlight the row on hover.

Implementing Row Selection

You can also implement row selection within Material UI V5 Tables, using the TableBodyProps and TableRowProps components. Here’s an example of how to select rows:

import { Checkbox, TableBodyProps } from '@mui/material';

function SelectableRowsTable() {
    const [selected, setSelected] = useState([]);

    const handleSelectAllClick = (event) => {
        if (event.target.checked) {
            const newSelected = data.map((n) => n.id);
            setSelected(newSelected);
            return;
        }
        setSelected([]);
    };

    const handleClick = (event, id) => {
        const selectedIndex = selected.indexOf(id);
        let newSelected = [];

        if (selectedIndex === -1) {
            newSelected = newSelected.concat(selected, id);
        } else if (selectedIndex === 0) {
            newSelected = newSelected.concat(selected.slice(1));
        } else if (selectedIndex === selected.length - 1) {
            newSelected = newSelected.concat(selected.slice(0, -1));
        } else if (selectedIndex > 0) {
            newSelected = newSelected.concat(
              selected.slice(0, selectedIndex),
              selected.slice(selectedIndex + 1),
            );
        }
        setSelected(newSelected);
    };

    const isSelected = (id) => selected.indexOf(id) !== -1;

    return (
        <TableContainer component={Paper}>
            <Table className={classes.table}>
                <TableHead>
                    <TableRow>
                        <TableCell padding="checkbox">
                            <Checkbox
                                indeterminate={selected.length > 0 && selected.length < data.length}
                                checked={selected.length === data.length}
                                onChange={handleSelectAllClick}
                                inputProps={{ 'aria-label': 'select all desserts' }}
                            />
                        </TableCell>
                        <TableCell>First Name</TableCell>
                        <TableCell>Last Name</TableCell>
                        <TableCell>Email</TableCell>
                    </TableRow>
                </TableHead>
                <TableBody component={TableBodyProps}>
                    {data.map((row) => {
                        const isItemSelected = isSelected(row.id);
                        const labelId = `enhanced-table-checkbox-${row.id}`;

                        return (
                            <TableRow
                                hover
                                onClick={(event) => handleClick(event, row.id)}
                                role="checkbox"
                                aria-checked={isItemSelected}
                                tabIndex={-1}
                                key={row.id}
                                selected={isItemSelected}
                                {...TableRowProps}
                            >
                                <TableCell padding="checkbox">
                                    <Checkbox
                                        checked={isItemSelected}
                                        inputProps={{ 'aria-labelledby': labelId }}
                                    />
                                </TableCell>
                                <TableCell>{row.firstName}</TableCell>
                                <TableCell>{row.lastName}</TableCell>
                                <TableCell>{row.email}</TableCell>
                            </TableRow>
                        );
                    })}
                </TableBody>
            </Table>
        </TableContainer>
    );
}

Here, the selected state stores an array of ids for the currently selected rows. The handleSelectAllClick function is called when the user clicks the “Select All” checkbox, which either selects all rows or clears the selection. The handleClick function is called when the user clicks on a row, and it either selects or deselects the row depending on its current state. The isSelected function checks whether a particular row is selected or not. Finally, we’ve added the selected and TableRowProps props to the TableRow component to visually highlight the selected rows.

Implementing Custom Cell Rendering

Material UI V5 Tables also provide the ability to customize the rendering of table cells, by using the TableCell component’s component prop.

function CustomCellTable() {
    const data = [
        { id: 1, firstName: 'John', lastName: 'Doe', email: '[email protected]', phone: '123-456-7890' },
        { id: 2, firstName: 'Jane', lastName: 'Doe', email: '[email protected]', phone: '234-567-8901' },
        { id: 3, firstName: 'Clark', lastName: 'Kent', email: '[email protected]', phone: '345-678-9012' },
        { id: 4, firstName: 'Diana', lastName: 'Prince', email: '[email protected]', phone: '456-789-0123' },
        { id: 5, firstName: 'Bruce', lastName: 'Wayne', email: '[email protected]', phone: '567-890-1234' },
        { id: 6, firstName: 'Tony', lastName: 'Stark', email: '[email protected]', phone: '678-901-2345' }
    ];

    const renderCell = (value, rowIndex, columnIndex) => {
        if (columnIndex === 4) {
            return <a href={`tel:${value}`}>{value}</a>;
        }
        return value;
    };

    return (
        <TableContainer component={Paper}>
            <Table className={classes.table}>
                <TableHead>
                    <TableRow>
                        <TableCell>First Name</TableCell>
                        <TableCell>Last Name</TableCell>
                        <TableCell>Email</TableCell>
                        <TableCell>Phone</TableCell>
                    </TableRow>
                </TableHead>
                <TableBody>
                    {data.map((row) => (
                        <TableRow key={row.id}>
                            <TableCell>{row.firstName}</TableCell>
                            <TableCell>{row.lastName}</TableCell>
                            <TableCell>{row.email}</TableCell>
                            <TableCell>
                                <TableCell component="th" scope="row">
                                    {renderCell(row.phone, row.id, 4)}
                                </TableCell>
                            </TableCell>
                        </TableRow>
                    ))}
                </TableBody>
            </Table>
        </TableContainer>
    );
}

In this example, we’ve defined a data array, each object representing a row of data. We’ve also defined a renderCell function that takes in a cell value, row index, and column index, and returns the appropriate JSX to render the cell. Here, we’re rendering a hyperlink for cell values in the “Phone” column.

Conclusion

Material UI V5 Tables provide a powerful and flexible way to display data in a web application. They offer sophisticated sorting, filtering, and pagination functionality that can be customized to meet specific requirements. Additionally, by leveraging the component-based architecture, developers can build complex and accessible user interfaces quickly and maintain them efficiently. With a solid understanding of the fundamentals, developers can build rich, responsive, and accessible data tables with Material UI V5 Tables.

Floating Action Buttons In Material UI V5
Material UI

Floating Action Buttons In Material UI V5

Introduction Hey there fellow developers! Have you ever wanted to create a floating action button that seamlessly integrates with your project’s design? Well, look no further than Material UI V5 floating action buttons! Floating action buttons, or FABs, have become a popular design element in web design due to their ease of use and functionality. […]

Alerts in Material UI V5
Material UI

Alerts in Material UI V5

Introduction Let’s take a look at Alerts In Material UI V5. With its sleek design and easy-to-use features Material UI V5 makes it easy to show your users alerts. Alerts are an essential element of any website or application, allowing us to provide users with important information and updates. In this article, we’re going to […]