Adding Meatballs Menu To React-Table Rows
Tags: programming, bootstrap, javascript, react
Published: 2023-02-06 16:46:35

---
title: Adding Meatballs Menu To React-Table Rows
published: true
date: 2023-02-06 16:46:17 UTC
cover_image: https://dev-to-uploads.s3.amazonaws.com/uploads/articles/tfifth26vg7gvks03dtk.png
tags: Programming,bootstrap,javascript,react
canonical_url: https://www.codejourney.net/adding-meatballs-menu-to-react-table-rows/
---

Meatballs menu (⋯), also called _three horizontal dots menu_, is a great way of providing contextual options for grid rows. In this article, I will show you how to add the meatballs menu to a table built with [@tanstack/react-table](https://www.npmjs.com/package/@tanstack/react-table).

After reading this article, you will know how to add such a menu to your React app. The end result will look as in the highlighted picture of this article 😉

## Creating a table with row selection support

First, let’s define a type of data that we want to display. For our example, we will display a list of `Car`s:

```tsx
// Car.ts
export type Car = {
  id: string;
  brand: string;
  model: string;
  productionYear: number;
  isAvailable: boolean;
};
```
 

Next, we create a new component called `CarsTable`, responsible for rendering the table. We will use [@tanstack/react-table](https://www.npmjs.com/package/@tanstack/react-table) for the table behavior and [react-bootstrap](https://www.npmjs.com/package/react-bootstrap) for UI elements.

I implemented the `CarsTable` component in [quite a standard way according to react-table docs](https://tanstack.com/table/v8/docs/examples/react/basic), so I won’t copy-paste it here. You can check the whole component’s code [here](https://github.com/dsibinski/react-table-context-menu/blob/faccde72b4be060c9dbca3d59d7b1506bc9f9b01/src/components/CarsTable.tsx). What’s interesting is that I added support for row selection in a way that makes our table a [controlled React component](https://reactjs.org/docs/forms.html#controlled-components):

```tsx
// CarsTable.tsx – controlled row selection 
type CarsTableProps = {
  selectedCar: Car | null;
  onCarSelected: (car: Car) => void;
};
export const CarsTable = (props: CarsTableProps) => {
  return (
    // ...
    <tbody>
        {table.getRowModel().rows.map((row) => {
          const isActive = row.original.id === props.selectedCar?.id;
          return (
            <tr
              key={row.id}
              style={isActive === true ? { backgroundColor: "#3a7a11" } : undefined}
              onClick={() => {
                props.onCarSelected(row.original);
              }}
            >
              {row.getVisibleCells().map((cell) => (
                <td key={cell.id}>
                  {flexRender(cell.column.columnDef.cell, cell.getContext())}
                </td>
              ))}
            </tr>
          );
        })}
      </tbody>
    // ...
  )
}
```
 
 

As you can see, `selectedCar` and `onCarSelected` are managed from outside. See settings the `style` - this is how the row color gets changed for the currently selected car. I recently had to deal with such a case in one of my projects.

So far, so good. This is how it looks, populated with sample data:

![react-table table with row selection support](https://i0.wp.com/www.codejourney.net/wp-content/uploads/2023/02/1_InitialTable.png?resize=991%2C440&ssl=1)

## Adding meatballs menu

Ok, we have a table. Now we want to add the meatballs menu. We need a three dots icon and a dropdown menu to open on clicking it.

After quickly [going through the react-bootstrap docs](https://react-bootstrap.github.io/components/dropdowns/), let’s create a new component for that:

 
```tsx
// CarRowContextMenu.tsx
import { Dropdown } from "react-bootstrap";
import { Car } from "../types/Car";
import CustomDivToggle from "./CustomDivToggle";
import { BsThreeDots } from "react-icons/bs";

export const CarRowContextMenu = ({ carRow }: { carRow: Car }) => {
  return (
    <Dropdown key={carRow.id}>
      <Dropdown.Toggle as={CustomDivToggle} style={{ cursor: "pointer" }}>
        <BsThreeDots />
      </Dropdown.Toggle>
      <Dropdown.Menu>
        <Dropdown.Item>Option 1</Dropdown.Item>
        <Dropdown.Item>Option 2</Dropdown.Item>
      </Dropdown.Menu>
    </Dropdown>
  );
};
```
 

As we want our dropdown toggle to have custom style, I had to provide `CustomDivToggle` as a [custom dropdown component](https://react-bootstrap.github.io/components/dropdowns/#custom-dropdown-components). It’s nothing very interesting, but you can check its implementation [here](https://github.com/dsibinski/react-table-context-menu/blob/c98687413a28eecfc5fa4175a4b55a79dee8300f/src/components/CustomDivToggle.tsx) 😉

Next, as we’d like our meatballs menu to be an additional column in the grid, it seems natural to [use `react-table`‘s display column](https://tanstack.com/table/v8/docs/guide/column-defs#column-def-types). Let’s try adding it:

 
```tsx
// CarsTable.tsx – adding meatballs menu with display-type column
columnHelper.display({
      id: "context-menu",
      cell: (cellContext) => {
        const row = cellContext.row.original;
        return <CarRowContextMenu carRow={row} />;
      },
    }),
``` 
 

It looks we have it:

![Meatballs menu added to the table with react-tabe's display column](https://i0.wp.com/www.codejourney.net/wp-content/uploads/2023/02/3_ContextMenuAddedV1.png?resize=984%2C442&ssl=1)

However, after clicking through it for a while, it seems we have an issue. The toggle only opens on every 2nd click:

![Meatballs menu with react-table's display column. Double-click issue](https://i0.wp.com/www.codejourney.net/wp-content/uploads/2023/02/4_ContextMenuDoubleClickIssue.gif?resize=1140%2C498&ssl=1)

Why is that? The reason is our controlled `CarsTable` component. On clicking a new row, the change event occurs, which triggers the re-render of the `CarsTable` component (because `selectedCar` _actually_ changes). It makes `react-table` re-render the table, with the meatballs menu in its default state (collapsed). On clicking the menu in the same row again, the change event occurs, but the `selectedCar` _does not actually change_, which _does not_ trigger the re-render. Initially, it took me a while to figure that out 😀

## Fixing double-click issue

In our case, a fix for the double click issue is quite simple. Instead of adding the column with the menu using `columnHelper.display()` function from `react-table`, we can render it _manually_. To do that, we should simply add a new `<td>` to each row of the table:

 
```tsx
// CarsTable.tsx – context menu added as a separate <td>
<tbody>
        {table.getRowModel().rows.map((row) => {
          const isActive = row.original.id === props.selectedCar?.id;
          return (
            <tr
              key={row.id}
              style={isActive === true ? { backgroundColor: "#3a7a11" } : undefined}
              onClick={() => {
                props.onCarSelected(row.original);
              }}
            >
              {row.getVisibleCells().map((cell) => (
                <td key={cell.id}>
                  {flexRender(cell.column.columnDef.cell, cell.getContext())}
                </td>
              ))}
              <td>
                <CarRowContextMenu carRow={row.original} />
              </td>
            </tr>
          );
        })}
</tbody>
``` 
 

Additionally, to make it work, we need an additional table’s header placeholder:

 
```tsx
// CarsTable.tsx – placeholder <th> for context menu
<thead>
        {table.getHeaderGroups().map((headerGroup) => (
          <tr key={headerGroup.id}>
            {headerGroup.headers.map((header) => (
              <th key={header.id}>
                {header.isPlaceholder
                  ? null
                  : flexRender(
                      header.column.columnDef.header,
                      header.getContext()
                    )}
              </th>
            ))}
            {/* placeholder header for context menu */}
            <th></th>
          </tr>
        ))}
</thead>
``` 
 
That’s it! Our `react-table` table with row selection support and the meatballs menu works like a charm now:

![react-table table with row selection and meatballs menu - final version](https://i0.wp.com/www.codejourney.net/wp-content/uploads/2023/02/5_ContextMenuFinalVersion-1.gif?resize=1140%2C493&ssl=1)

## Meatballs menu with react-table – source code

You can find the complete [source code here](https://github.com/dsibinski/react-table-context-menu). I hope you find it useful! 🙂