Episode ini memperkaya TanStack Table dengan fitur enterprise: column visibility dan grouping, aggregation dengan fungsi sum, row selection dan expansion, virtualized table, serta custom cell rendering untuk tampilan data yang dinamis.

Episode 6 membangun tabel dasar. Sekarang kita masuk ke fitur yang membedakan tabel sederhana dari datagrid enterprise: kolom yang bisa disembunyikan, pengelompokan dengan agregasi, pemilihan baris, ekspansi, serta render sel custom.
Episode 9 membahas column visibility, grouping dan aggregation, row selection dan expansion, virtualized tables, serta custom cell rendering. Semua fitur ini diaktifkan dengan menambahkan row model dan state — bukan dengan menulis ulang logika.
Ini juga episode pertama di mana TanStack Table dan TanStack Virtual bekerja sama, membuka jalan untuk tabel dengan ratusan ribu baris.
Column visibility diatur dengan state columnVisibility. Toggle kolom tinggal memanggil column.toggleVisibility:
const [columnVisibility, setColumnVisibility] = useState({})
const table = useReactTable({
data,
columns,
state: { columnVisibility },
onColumnVisibilityChange: setColumnVisibility,
getCoreRowModel: getCoreRowModel(),
})state dan onColumnVisibilityChange menjadikan visibility sebagai controlled state. Kalian bisa merender checkbox per kolom yang memanggil column.toggleVisibility(), sehingga user mengontrol kolom mana yang tampil tanpa mengubah definisi kolom.
Grouping mengelompokkan baris berdasarkan nilai kolom tertentu. Aktifkan lewat getGroupedRowModel, lalu panggil column.getToggleGroupingHandler() pada header:
const table = useReactTable({
data,
columns,
state: { grouping },
onGroupingChange: setGrouping,
getCoreRowModel: getCoreRowModel(),
getGroupedRowModel: getGroupedRowModel(),
getExpandedRowModel: getExpandedRowModel(),
})getGroupedRowModel() mengubah data menjadi baris grup, dan getExpandedRowModel() mengelola baris grup yang dibuka. Kelompok bisa dibuka dan ditutup lewat row.getIsExpanded() dan row.getToggleExpandedHandler().
Saat data di-group, kolom angka sering perlu diagregasi. TanStack Table menyediakan aggregationFn bawaan seperti sum, min, max, dan count:
columnHelper.accessor("gaji", {
header: "Gaji",
aggregationFn: "sum",
aggregatedCell: ({ getValue }) => `Total: ${getValue()}`,
})aggregationFn: "sum" menghitung jumlah nilai di dalam tiap grup. aggregatedCell menentukan bagaimana nilai agregat dirender — di sini sebagai Total. Di baris grup, kolom ini menampilkan jumlah; di baris detail, menampilkan nilai asli.
Row selection aktif dengan enableRowSelection. Tambahkan kolom checkbox memakai columnHelper.display:
columnHelper.display({
id: "pilih",
header: ({ table }) => (
<input
type="checkbox"
checked={table.getIsAllRowsSelected()}
onChange={table.getToggleAllRowsSelectedHandler()}
/>
),
cell: ({ row }) => (
<input
type="checkbox"
checked={row.getIsSelected()}
onChange={row.getToggleSelectedHandler()}
/>
),
})columnHelper.display membuat kolom tanpa data — khusus untuk aksi seperti checkbox. row.getIsSelected() dan row.getToggleSelectedHandler() mengelola status tiap baris; table.getIsAllRowsSelected() mengelola select-all.
Untuk data besar, kombinasikan TanStack Table dengan TanStack Virtual. Baris yang dirender diganti item virtual:
const table = useReactTable({
data,
columns,
getCoreRowModel: getCoreRowModel(),
})
const virtualizer = useVirtualizer({
count: table.getRowModel().rows.length,
getScrollElement: () => parentRef.current,
estimateSize: () => 40,
})
const rows = table.getRowModel().rows
const virtualRows = virtualizer.getVirtualItems()virtualizer.getVirtualItems() mengembalikan hanya baris yang terlihat. Saat merender, baris virtual ditempatkan secara absolute dengan translateY, dan tinggi total container di-set ke virtualizer.getTotalSize(). Detail penuh virtualisasi akan dibahas di episode 11.
cell pada definisi kolom bisa berupa fungsi yang mengembalikan elemen apa pun. Ini membuat tabel mendukung badge, tombol, link, bahkan chart kecil:
columnHelper.accessor("status", {
header: "Status",
cell: ({ getValue }) => {
const status = getValue()
return (
<span className={status === "aktif" ? "badge-hijau" : "badge-merah"}>
{status}
</span>
)
},
})cell: ({ getValue }) => ... menerima konteks dan mengembalikan elemen React. Karena rendering sepenuhnya milik kalian, desain tabel hanya dibatasi imajinasi — bukan oleh API library.
Info
Gabungkan virtualized table dengan getRowCanExpand untuk ekspansi baris berisi tabel atau formulir bertingkat. Episode 23 memakai kombinasi ini dalam aplikasi penuh.
Episode 9 memperkaya tabel kalian: column visibility dengan controlled state, grouping dengan agregasi, row selection lewat checkbox, virtualized table untuk data raksasa, dan custom cell rendering untuk tampilan bebas.
Inti yang harus dibawa pulang:
Di episode 10 selanjutnya kita akan membahas router advanced dan data management — deferred data loading dengan Await, error boundaries dan notFound, route-based mutations dengan invalidasi router, serta full-stack routing patterns dengan loader data. Router kalian akan belajar menangani data berat dan error!