TanStack Table পরিচিতি (Headless Table)
Headless মানে কী, TanStack Table v8 কেন এলো, v7 থেকে কী বদলালো, কখন ব্যবহার করবেন আর কখন করবেন না — বাংলায়।
একটা টেবিল বানানো কি কঠিন? <table>, <tr>, <td> — তিনটা ট্যাগেই হয়ে গেল। কিন্তু একবার লাখ-লাখ row দেখাতে হবে, কলাম লুকাতে হবে, সাজাতে হবে, খুঁজতে হবে, পেজ ভাগ করতে হবে, সার্ভার থেকে ডাটা আনতে হবে — তখনই বোঝা যায় আসল সমস্যা কোথায়। TanStack Table ঠিক এই সমস্যাটাই সমাধান করে।
Headless মানে কী?
headless = খালি মাথা। অর্থাৎ লাইব্রেরি আপনাকে একটা চেহারা দেয় না। এটি শুধু state ও logic দেয় — কোন row সাজানো হবে, কোনটা বেছে নেওয়া হয়েছে, কত পেজ আছে — আর বাকি সব (HTML, CSS, class name) আপনি লিখবেন।
// এটা TanStack Table নয় — এটা শুধু আপনার markup
<table className="w-full text-sm">
<thead className="bg-muted">
<tr>
{table.getHeaderGroups().map((group) => (
<th key={group.id} className="p-3 text-left">
{group.headers.map((header) =>
flexRender(
header.column.columnDef.header,
header.getContext(),
),
)}
</th>
))}
</tr>
</thead>
<tbody>
{table.getRowModel().rows.map((row) => (
<tr key={row.id} className="border-b">
{row.getVisibleCells().map((cell) => (
<td key={cell.id} className="p-3">
{flexRender(cell.column.columnDef.cell, cell.getContext())}
</td>
))}
</tr>
))}
</tbody>
</table>উপরের table, flexRender, getHeaderGroups, getVisibleCells — এই সব TanStack Table থেকে এসেছে। কিন্তু <table>, <th>, className="p-3" — এগুলো সম্পূর্ণ আপনার।
| Material UI DataGrid | TanStack Table | |
|---|---|---|
| চেহারা | লাইব্রেরির (আপনি ফোরক করেন) | আপনার (আপনি লিখেন) |
| bundle | বড় | ছোট, tree-shakeable |
| column resize/visibility | preset | নিজের মতো কাস্টমাইজ |
| server-side | হ্যান্ডল করে না | manual* মোড দেয় |
| শেখার বাধা | কম (সব দেওয়া) | বেশি (সব আপনাকে করতে হবে) |
v8 কেন এলো?
v8 হলো সম্পূর্ণ নতুন rewrite — ইচ্ছাকৃতভাবে, পিছনের সামঞ্জস্য ধরে রাখা হয়নি। মূল পরিবর্তন কয়েকটি।
1. Features আলাদা করা
আগে (v7) সব feature একটা বিশাল useTable hook-এ ঢোকানো ছিল। v8-এ প্রতিটি feature আলাদা row model হিসেবে আসে — আপনি শুধু যেটা দরকার সেটাই দেন।
// v8 — কম প্যাকেজ, কম কাজ
useReactTable({
data,
columns,
getCoreRowModel: getCoreRowModel(), // বেসিক — এটা ছাড়া আর কিছু চলে না
})sorting/filtering/pagination চাইলে getSortedRowModel(), getFilteredRowModel(), getPaginationRowModel() যোগ করবেন — ব্যস, ঐগুলোর কোড বান্ডেলেও আসবে না।
2. সব API get* / set* ছাড়ালো
// v7
useReactTable({ manualSort: true })
header.column.toggleSorting()
table.setPageIndex(0)
// v8 — আরও সুসংগত
useReactTable({ manualSorting: true })
header.column.getToggleSortingHandler()
table.setPageIndex(0)
// v8-এ যা যা তার উপর generic
table.getState()
table.setColumnSizing()
table.optionsv8-এ প্রতিটি row, column ও cell ইত্যাদি instance পায় — তাই row.getIsSelected(), column.getIsVisible(), cell.getContext() লিখে state ও action পাওয়া যায়। v7-এ এগুলো table.state-এ ফাঁকা জায়গা ছিল।
3. TypeScript-প্রেমি
ColumnDef<TData>-এর সব option আগে থেকেই টাইপ করা। নতুন কোনো option ভুলে গেলে কম্পাইলারই ধরিয়ে দেবে। v7-এর loose typing-এর তুলনায় এটি বড় লাভ।
কখন ব্যবহার করবেন
- Admin dashboard — user, order, product সবচেয়ে common
- যেখানে sorting/filtering/pagination আছে — এগুলো নিজে থেকেই পাওয়া যায়
- যেখানে নিজের ডিজাইন দরকার — sticky header, ছোট কলাম, মোবাইলে কলাম লুকানো
- Server-side data —
manual*মোড ওrowCountদেয়
কখন ব্যবহার করবেন না
- খুব ছোট, স্থির তালিকা — ৫টা item, header-এ কোনো action নেই? সাধারণ
.map()-ই যথেষ্ট - Ready-made চাইলে — Material UI, AG Grid Enterprise — সেগুলোতে অনেক কিছু বানানো
- Editable grid (spreadsheet) লাগলে — AG Grid-এর সেটা অনেক বেশি mature
TanStack state ও logic দেয়, markup-ওর দায়িত্ব আপনার — loading, empty state, এমনকি aria-label পর্যন্ত আপনাকেই লিখতে হবে। এটাই মূল চুক্তি: লাইব্রেরি যা করতে পারে না, সেটা আপনি করেন।
পরের অধ্যায় থেকে আমরা একটি বাস্তব Product ডেটাবেস দিয়ে ধাপে ধাপে একটা পূর্ণাঙ্গ data table বানাবো — client-side sorting, filtering, pagination থেকে শুরু করে column resizing, row selection ও server-side mode পর্যন্ত। প্রতিটি অধ্যায় আগেরটির উপর দাঁড়ায়, তাই কোডগুলো সরাসরি যোগ করতে পারবেন।
পরবর্তী
পরের অধ্যায়ে প্রজেক্ট সেটআপ — প্যাকেজ ইনস্টল, প্রয়োজনীয় shadcn/ui কম্পোনেন্ট এবং ফোল্ডার স্ট্রাকচার।