Skip to main content

Table Virtuoso integrated with @tanstack/table

The following takes the virtualized rows example of @tanstack/table and instead of using @tanstack/virtual it uses Table Virtoso.

Virtualized @tanstack/table with Table Virtuoso

Live Editor

function makeData(count) {
  return Array.from({length: count}, (_, index) => {
  const firstName = `User ${index}`
  const lastName = "Doe";
    return {
      id: index,
      firstName,
      lastName,
      age: Math.floor(Math.random() * 100),
      visits: Math.floor(Math.random() * 100),
      progress: Math.floor(Math.random() * 100),
      status: ["relationship", "complicated", "single"][Math.floor(Math.random() * 3)],
    };
  })
}

function App() {
  const rerender = useReducer(() => ({}), {})[1];

  const [sorting, setSorting] = useState([]);

  const columns = React.useMemo(
    () => [
      {
        accessorKey: "firstName",
        cell: (info) => info.getValue()
      },
      {
        accessorFn: (row) => row.lastName,
        id: "lastName",
        cell: (info) => info.getValue(),
        header: () => <span>Last Name</span>
      },
      {
        accessorKey: "age",
        header: () => "Age",
        size: 50
      },
      {
        accessorKey: "visits",
        header: () => <span>Visits</span>,
        size: 50
      },
      {
        accessorKey: "status",
        header: "Status"
      },
      {
        accessorKey: "progress",
        header: "Profile Progress",
        size: 80
      }
    ],
    []
  );

  const [data, setData] = React.useState(() => makeData(500));
  const refreshData = () => setData(() => makeData(500));

  const table = Tanstack.useReactTable({
    data,
    columns,
    state: {
      sorting
    },
    onSortingChange: setSorting,
    getCoreRowModel: Tanstack.getCoreRowModel(),
    getSortedRowModel: Tanstack.getSortedRowModel()
  });

  const { rows } = table.getRowModel();

  return (
    <div style={{ padding: "0.5rem" }}>
      <div style={{ height: "0.5rem" }} />

      <TableVirtuoso
        style={{ height: "500px", border: "1px solid lightgray" }}
        totalCount={rows.length}
        components={{
          Table: ({ style, ...props }) => {
            return (
              <table
                {...props}
                style={{
                  ...style,
                  width: "100%",
                  tableLayout: "fixed",
                  borderCollapse: "collapse",
                  borderSpacing: 0
                }}
              />
            );
          },
          TableRow: (props) => {
            const index = props["data-index"];
            const row = rows[index];

            return (
              <tr {...props}>
                {row.getVisibleCells().map((cell) => (
                  <td key={cell.id} style={{ padding: "6px" }}>
                    {Tanstack.flexRender(cell.column.columnDef.cell, cell.getContext())}
                  </td>
                ))}
              </tr>
            );
          }
        }}
        fixedHeaderContent={() => {
          return table.getHeaderGroups().map((headerGroup) => (
            <tr
              key={headerGroup.id}
              style={{ background: "lightgray", margin: 0 }}
            >
              {headerGroup.headers.map((header) => {
                return (
                  <th
                    key={header.id}
                    colSpan={header.colSpan}
                    style={{
                      width: header.getSize(),
                      borderBottom: "1px solid lightgray",
                      padding: "2px 4px",
                      textAlign: "left"
                    }}
                  >
                    {header.isPlaceholder ? null : (
                      // eslint-disable-next-line jsx-a11y/click-events-have-key-events
                      <div
                        {...{
                          style: header.column.getCanSort()
                            ? { cursor: "pointer", userSelect: "none" }
                            : {},
                          onClick: header.column.getToggleSortingHandler()
                        }}
                      >
                        {Tanstack.flexRender(
                          header.column.columnDef.header,
                          header.getContext()
                        )}
                        {{
                          asc: " 🔼",
                          desc: " 🔽"
                        }[header.column.getIsSorted()] ?? null}
                      </div>
                    )}
                  </th>
                );
              })}
            </tr>
          ));
        }}
      />

      <div>{table.getRowModel().rows.length} Rows</div>
      <div>
        <button onClick={() => rerender()}>Force Rerender</button>
      </div>
      <div>
        <button onClick={() => refreshData()}>Refresh Data</button>
      </div>
    </div>
  );
}

render(<App />);
Result
Loading...