Please check this demo
Idea is array of data set, We are going to render inside table should know, what are the cells going to use row span.
This case, individual person has category and that and "categorySpan"
const defaultData: Person[] = [
{
category:"category one",
categorySpan:4,
firstName: 'tanner',
lastName: 'linsley',
age: 24,
visits: 100,
status: 'In Relationship',
progress: 50,
},
{
category:"category one",
firstName: 'tandy',
lastName: 'miller',
age: 40,
visits: 40,
status: 'Single',
progress: 80,
},
{
category:"category one",
firstName: 'joe',
lastName: 'dirte',
age: 45,
visits: 20,
status: 'Complicated',
progress: 10,
}
,
{
category:"category one",
firstName: 'joe',
lastName: 'dirte',
age: 45,
visits: 20,
status: 'Complicated',
progress: 10,
}
,
{
category:"category two",
categorySpan:2,
firstName: 'joe',
lastName: 'dirte',
age: 45,
visits: 20,
status: 'Complicated',
progress: 10,
}
,
{
category:"category two",
firstName: 'joe',
lastName: 'dirte',
age: 45,
visits: 20,
status: 'Complicated',
progress: 10,
}
,
{
category:"category three",
categorySpan:1,
firstName: 'joe',
lastName: 'dirte',
age: 45,
visits: 20,
status: 'Complicated',
progress: 10,
}
,
{
category:"category four",
categorySpan:1,
firstName: 'joe',
lastName: 'dirte',
age: 45,
visits: 20,
status: 'Complicated',
progress: 10,
}
]
So you can add row span property to td base on category and category span
<tr key={row.id}>
{row.getVisibleCells().map(cell => {
if(cell.column.id=="category"){
if(row.original.categorySpan){
return ( <td key={cell.id} rowSpan={row.original.categorySpan}>
{flexRender(cell.column.columnDef.cell, cell.getContext())}
</td>);
}
}
else{
return (
<td key={cell.id}>
{flexRender(cell.column.columnDef.cell, cell.getContext())}
</td>
)
}
})}
</tr>
Final code will be like this
import * as React from 'react'
import ReactDOM from 'react-dom/client'
import './index.css'
import {
createColumnHelper,
flexRender,
getCoreRowModel,
useReactTable,
} from '@tanstack/react-table'
type Person = {
category:string
categorySpan?:number
firstName: string
lastName: string
age: number
visits: number
status: string
progress: number
}
const defaultData: Person[] = [
{
category:"category one",
categorySpan:4,
firstName: 'tanner',
lastName: 'linsley',
age: 24,
visits: 100,
status: 'In Relationship',
progress: 50,
},
{
category:"category one",
firstName: 'tandy',
lastName: 'miller',
age: 40,
visits: 40,
status: 'Single',
progress: 80,
},
{
category:"category one",
firstName: 'joe',
lastName: 'dirte',
age: 45,
visits: 20,
status: 'Complicated',
progress: 10,
}
,
{
category:"category one",
firstName: 'joe',
lastName: 'dirte',
age: 45,
visits: 20,
status: 'Complicated',
progress: 10,
}
,
{
category:"category two",
categorySpan:2,
firstName: 'joe',
lastName: 'dirte',
age: 45,
visits: 20,
status: 'Complicated',
progress: 10,
}
,
{
category:"category two",
firstName: 'joe',
lastName: 'dirte',
age: 45,
visits: 20,
status: 'Complicated',
progress: 10,
}
,
{
category:"category three",
categorySpan:1,
firstName: 'joe',
lastName: 'dirte',
age: 45,
visits: 20,
status: 'Complicated',
progress: 10,
}
,
{
category:"category four",
categorySpan:1,
firstName: 'joe',
lastName: 'dirte',
age: 45,
visits: 20,
status: 'Complicated',
progress: 10,
}
]
const columnHelper = createColumnHelper<Person>()
const columns = [
columnHelper.accessor('category', {
cell: info => info.getValue(),
footer: info => info.column.id,
}),
columnHelper.accessor('firstName', {
cell: info => info.getValue(),
footer: info => info.column.id,
}),
columnHelper.accessor(row => row.lastName, {
id: 'lastName',
cell: info => <i>{info.getValue()}</i>,
header: () => <span>Last Name</span>,
footer: info => info.column.id,
}),
columnHelper.accessor('age', {
header: () => 'Age',
cell: info => info.renderValue(),
footer: info => info.column.id,
}),
columnHelper.accessor('visits', {
header: () => <span>Visits</span>,
footer: info => info.column.id,
}),
columnHelper.accessor('status', {
header: 'Status',
footer: info => info.column.id,
}),
columnHelper.accessor('progress', {
header: 'Profile Progress',
footer: info => info.column.id,
}),
]
function App() {
const [data, setData] = React.useState(() => [...defaultData])
const rerender = React.useReducer(() => ({}), {})[1]
const table = useReactTable({
data,
columns,
getCoreRowModel: getCoreRowModel(),
})
return (
<div className="p-2">
<table>
<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>
))}
</tr>
))}
</thead>
<tbody>
{table.getRowModel().rows.map(row => (
<tr key={row.id}>
{row.getVisibleCells().map(cell => {
if(cell.column.id=="category"){
if(row.original.categorySpan){
return ( <td key={cell.id} rowSpan={row.original.categorySpan}>
{flexRender(cell.column.columnDef.cell, cell.getContext())}
</td>);
}
}
else{
return (
<td key={cell.id}>
{flexRender(cell.column.columnDef.cell, cell.getContext())}
</td>
)
}
})}
</tr>
))}
</tbody>
</table>
<div className="h-4" />
<button onClick={() => rerender()} className="border p-2">
Rerender
</button>
</div>
)
}
const rootElement = document.getElementById('root')
if (!rootElement) throw new Error('Failed to find the root element')
ReactDOM.createRoot(rootElement).render(
<React.StrictMode>
<App />
</React.StrictMode>
)