How to use rowSpan? #2233
-
| I made a minimal repro of my original code. Here it is in HTML: <!DOCTYPE html>
<html>
<head>
<style>
table, th, td { 
  text-align: center;
  border: 1px dashed lightpink;
  color: navy;
}
</style>
</head>
<body>
<table>
  <tr>
    <th>Actor</th>
    <th>Movies</th>
  </tr>
  <tr>
    <td rowspan="4">Johnny Depp</td>
    <td>Pirates of the Carribean  1</td>
  </tr>
  <tr>
    <td>Pirates of the Carribean  2</td>
  </tr>
  <tr>
    <td>Pirates of the Carribean  3</td>
  </tr>
  <tr>
    <td>Pirates of the Carribean  4</td>
  </tr>
</table>
</body>
</html>The output looks like: So I tried implementing it in CodeSandBox. Following is the code: import * as React from "react";
import { useTable } from "react-table";
const borderStyle = {
  border: "1px dashed navy"
};
export default function App() {
  const data = React.useMemo(
    () => [
      {
        actor: "Johnny Depp",
        movies: [
          {
            name: "Pirates of the Carribean 1"
          },
          {
            name: "Pirates of the Carribean 2"
          },
          {
            name: "Pirates of the Carribean 3"
          },
          {
            name: "Pirates of the Carribean 4"
          }
        ]
      }
    ],
    []
  );
  const columns = React.useMemo(
    () => [
      {
        Header: "Actor",
        accessor: "actor",
        enableRowSpan: true
      },
      {
        Header: "Movies",
        accessor: (row, index) => {
          console.log({ row });
          return row.movies.map(movie => movie.name);
        }
      }
    ],
    []
  );
  const {
    getTableProps,
    getTableBodyProps,
    headerGroups,
    rows,
    prepareRow
  } = useTable({ columns, data });
  return (
    <table {...getTableProps()}>
      <thead>
        {headerGroups.map(headerGroup => (
          <tr {...headerGroup.getHeaderGroupProps()}>
            {headerGroup.headers.map(column => (
              <th {...column.getHeaderProps()} style={borderStyle}>
                {column.render("Header")}
              </th>
            ))}
          </tr>
        ))}
      </thead>
      <tbody {...getTableBodyProps()}>
        {rows.map((row, i) => {
          prepareRow(row);
          if (i == 0) {
            console.log({ row });
          }
          return (
            <tr {...row.getRowProps()}>
              {row.cells.map((cell, j) => {
                if (i == 0 && j < 2) {
                  console.log({ cell, i, j });
                }
                return (
                  <td
                    rowSpan={cell.rowSpan}
                    {...cell.getCellProps()}
                    style={borderStyle}
                  >
                    {cell.render("Cell")}
                  </td>
                );
              })}
            </tr>
          );
        })}
      </tbody>
    </table>
  );
}Here's the direct link to it: https://codesandbox.io/s/modest-sanderson-z0keq?file=/src/App.tsx It looks like: I want movie names to be one below the other but can't seem to figure it out 🤔 I want it to look like the HTML. The data shouldn't change as my original use-case fetches from an API. How do I do it? | 
Beta Was this translation helpful? Give feedback.
Replies: 10 comments 29 replies
-
| There is no rowspan hook included in react-table currently. So while you are setting rowSpanEnabled on the header and referencing cell.rowSpan, you aren't ever setting cell.rowSpan or checking rowSpanEnabled again. | 
Beta Was this translation helpful? Give feedback.
-
| @liberza I got that. I left that part out purposely because Idk how to display the movie names row by row. My movie list is an array of objects so how will I display it beside actor name? So it looks like: 
 Once I fix that, I can totally see myself copying your Codesandbox ;) | 
Beta Was this translation helpful? Give feedback.
-
| Okay made it work. Guess I just had to flatten the data. It was as easy as just copying & pasting your code. Here's the working solution: import * as React from "react";
import { useTable } from "react-table";
type Data = {
  actor: string;
  movie: string;
};
const borderStyle = {
  border: "1px solid gray",
  padding: "8px 10px"
};
function useInstance(instance) {
  const { allColumns } = instance;
  let rowSpanHeaders = [];
  allColumns.forEach((column, i) => {
    const { id, enableRowSpan } = column;
    if (enableRowSpan !== undefined) {
      rowSpanHeaders = [
        ...rowSpanHeaders,
        { id, topCellValue: null, topCellIndex: 0 }
      ];
    }
  });
  Object.assign(instance, { rowSpanHeaders });
}
export default function App() {
  const origData = [
    {
      actor: "Johnny Depp",
      movies: [
        {
          name: "Pirates of the Carribean 1"
        },
        {
          name: "Pirates of the Carribean 2"
        },
        {
          name: "Pirates of the Carribean 3"
        },
        {
          name: "Pirates of the Carribean 4"
        }
      ]
    }
  ];
  const newData: Array<Data> = [];
  origData.forEach(actorObj => {
    actorObj.movies.forEach(movie => {
      newData.push({
        actor: actorObj.actor,
        movie: movie.name
      });
    });
  });
  const data = React.useMemo(() => newData, []);
  const columns = React.useMemo(
    () => [
      {
        Header: "Actor",
        accessor: "actor",
        enableRowSpan: true
      },
      {
        Header: "Movies",
        accessor: "movie"
      }
    ],
    []
  );
  const {
    getTableProps,
    getTableBodyProps,
    headerGroups,
    rows,
    prepareRow,
    rowSpanHeaders
  } = useTable({ columns, data }, hooks => {
    hooks.useInstance.push(useInstance);
  });
  return (
    <table {...getTableProps()}>
      <thead>
        {headerGroups.map(headerGroup => (
          <tr {...headerGroup.getHeaderGroupProps()}>
            {headerGroup.headers.map(column => (
              <th {...column.getHeaderProps()} style={borderStyle}>
                {column.render("Header")}
              </th>
            ))}
          </tr>
        ))}
      </thead>
      <tbody {...getTableBodyProps()}>
        {rows.map((row, i) => {
          prepareRow(row);
          for (let j = 0; j < row.allCells.length; j++) {
            let cell = row.allCells[j];
            let rowSpanHeader = rowSpanHeaders.find(
              x => x.id === cell.column.id
            );
            if (rowSpanHeader !== undefined) {
              if (
                rowSpanHeader.topCellValue === null ||
                rowSpanHeader.topCellValue !== cell.value
              ) {
                cell.isRowSpanned = false;
                rowSpanHeader.topCellValue = cell.value;
                rowSpanHeader.topCellIndex = i;
                cell.rowSpan = 1;
              } else {
                rows[rowSpanHeader.topCellIndex].allCells[j].rowSpan++;
                cell.isRowSpanned = true;
              }
            }
          }
          return null;
        })}
        {rows.map(row => {
          return (
            <tr {...row.getRowProps()}>
              {row.cells.map(cell => {
                if (cell.isRowSpanned) return null;
                else
                  return (
                    <td
                      style={borderStyle}
                      rowSpan={cell.rowSpan}
                      {...cell.getCellProps()}
                    >
                      {cell.render("Cell")}
                    </td>
                  );
              })}
            </tr>
          );
        })}
      </tbody>
    </table>
  );
}And here's the link to Codesandbox 👉 https://codesandbox.io/s/flattening-row-with-react-table-prkqb?file=/src/App.tsx Thank you @liberza 🙌 | 
Beta Was this translation helpful? Give feedback.
-
| @liberza I found 1 problem with your solution. Guess it might be an edge-case. But here's the problem. I have 2 columns that I have to  As the 2nd one is a  Here's a visual: 
 So the  The problem comes while spanning  Let me know if you have any solutions in this regard? Also, keep this use-case in mind while making the plugin, I guess it'll be a common one. Just a hunch :) | 
Beta Was this translation helpful? Give feedback.
-
| Is there any way by which we can have row span on the table header, like the one below | 
Beta Was this translation helpful? Give feedback.
-
| I was wondering if there's a way to use  | 
Beta Was this translation helpful? Give feedback.
-
Beta Was this translation helpful? Give feedback.
-
| @deadcoder0904  Is there a way to achieve this for colspan ? | 
Beta Was this translation helpful? Give feedback.
-
| What about the support for the same in v8? Is there any roadmap for this? | 
Beta Was this translation helpful? Give feedback.
-
| Hey everyone! Just solved the header rowspan problem and packaged it into a tiny npm library. It's really simple to use — you can rowspan both the header and footer, with table and non-table tr/th elements. Check it out! https://www.npmjs.com/package/tanstack-table-header-rowspan it's about header and footer, not data rows. I probably could implement similar approach with data rows, but need to think more about handling pagination, virtualization and dynamic rows height | 
Beta Was this translation helpful? Give feedback.




Okay made it work. Guess I just had to flatten the data. It was as easy as just copying & pasting your code. Here's the working solution: