ইনস্টলেশন ও প্রজেক্ট সেটআপ
@tanstack/react-table ইনস্টল, shadcn/ui কম্পোনেন্ট, TypeScript টাইপ, ফোল্ডার স্ট্রাকচার ও পরীক্ষা করার সহজ উপায় — বাংলায়।
আগের অধ্যায়ে জানলাম headless মানে কী। এখন প্রজেক্ট দাঁড় করাই। TanStack Table-এর নিজস্ব কোনো UI নেই, তাই পাশে আমাদের shadcn/ui-র কম্পোনেন্ট লাগবে।
১. প্যাকেজ ইনস্টল
pnpm add @tanstack/react-tableশুধু এই একটাই প্যাকেজ লাগবে। v8-এ tree-shaking ভালো, তাই ব্যবহার না করা feature বান্ডেলে ঢোকে না।
Next.js App Router-এ যেকোনো table বানানোর আগে "use client" দরকার — কারণ useReactTable একটি hook।
২. shadcn/ui কম্পোনেন্ট
TanStack কোনো মার্কআপ দেয় না, তাই টেবিলের ভিত্তি বানাতে shadcn-এর table দরকার। পরের অধ্যায়গুলোর উদাহরণে আরও কিছু কম্পোনেন্ট ব্যবহার হবে, তাই সেগুলোও এখনই নিয়ে নিই।
npx shadcn@latest add table checkbox dropdown-menu command input select popover| কম্পোনেন্ট | কোথায় লাগবে |
|---|---|
table | টেবিলের ভিত্তি — Table, TableHeader, TableRow, TableCell |
checkbox | row selection এর checkbox |
dropdown-menu | column visibility / column pinning মেনু |
command | column visibility-এর সার্চ-বক্স |
input | global search বক্স |
select | page size বেছে নেওয়া |
popover | filter ড্রপডাউন |
// components/ui/table.tsx — shadcn থেকে পাওয়া
import {
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow,
} from "@/components/ui/table";৩. ডেটা টাইপ
পুরো সিরিজে আমরা একটি Product মডেল ব্যবহার করবো। সব অধ্যায়ে এটাই থাকবে, তাই কোডগুলো একটার সাথে আরেকটা জোড়া লাগবে।
// types/product.ts
export type Product = {
id: string;
name: string;
category: "electronics" | "clothing" | "books";
price: number;
stock: number;
rating: number;
createdAt: string; // ISO string
isActive: boolean;
};ডেটাবেসের id সাধারণত number হলেও string করে রাখুন। এতে getRowId: (row) => row.id লিখলে TypeScript-এর জন্য String(row.id) লেখা লাগে না, আর server-side mode-এ query param হিসেবে পাঠানোও সহজ হয়।
৪. মক ডেটা
উদাহরণ দেখানোর জন্য কিছু sample data — পরে এগুলো API থেকে আসবে।
// lib/mock-products.ts
export const mockProducts: Product[] = [
{
id: "p-01",
name: "Lenovo ThinkPad X1",
category: "electronics",
price: 185000,
stock: 12,
rating: 4.8,
createdAt: "2025-01-12T08:00:00.000Z",
isActive: true,
},
{
id: "p-02",
name: "জামালপুর শাড়ি",
category: "clothing",
price: 2450,
stock: 0,
rating: 4.1,
createdAt: "2025-02-03T10:30:00.000Z",
isActive: false,
},
// … আরও ১০টা row
];rating: 4.8 ও price: 185000-এর দশমিক ঘর বদলানো বোঝাতে formatRating / formatPrice নামে helper বানিয়ে রাখা ভালো — পরের অধ্যায়ে cell রেন্ডারে লাগবে।
৫. ফোল্ডার স্ট্রাকচার
components/
data-table/
data-table.tsx ← পুরো টেবিল (state + সব row model)
data-table-toolbar.tsx ← search, filter, column visibility
columns.tsx ← ColumnDef অ্যারে
cell/ ← নিজস্ব cell রেন্ডারার
price-cell.tsx
actions-cell.tsx
row-selection.tsx ← header checkbox
pagination.tsx ← পেজ কন্ট্রোল
lib/
mock-products.ts
types/
product.tsএই স্প্লিটই গুরুত্বপূর্ণ: columns.tsx-এ কলামের সংজ্ঞা, data-table.tsx-এ টেবিলের state। ফলে কলাম বদলালে টেবিলের কোড নড়ে না।
৬. প্রথম টেবিল — সব ফিচার বাদ
ইনস্টল শেষ, এবার নিশ্চিত হওয়ার মতো সবচেয়ে ছোট একটা টেবিল বানাই। এতে কোনো sorting/filtering নেই — শুধু data দেখানো। বাকি ফিচারগুলো পরের অধ্যায়ে।
"use client";
import {
flexRender,
getCoreRowModel,
useReactTable,
type ColumnDef,
} from "@tanstack/react-table";
import {
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow,
} from "@/components/ui/table";
import type { Product } from "@/types/product";
import { mockProducts } from "@/lib/mock-products";
export function DataTable() {
const columns: ColumnDef<Product>[] = [
{ accessorKey: "name", header: "পণ্যের নাম" },
{ accessorKey: "price", header: "মূল্য" },
];
const table = useReactTable({
data: mockProducts,
columns,
getCoreRowModel: getCoreRowModel(),
});
return (
<Table>
<TableHeader>
{table.getHeaderGroups().map((group) => (
<TableRow key={group.id}>
{group.headers.map((header) => (
<TableHead key={header.id}>
{header.isPlaceholder
? null
: flexRender(
header.column.columnDef.header,
header.getContext(),
)}
</TableHead>
))}
</TableRow>
))}
</TableHeader>
<TableBody>
{table.getRowModel().rows.map((row) => (
<TableRow key={row.id}>
{row.getVisibleCells().map((cell) => (
<TableCell key={cell.id}>
{flexRender(cell.column.columnDef.cell, cell.getContext())}
</TableCell>
))}
</TableRow>
))}
</TableBody>
</Table>
);
}import { DataTable } from "@/components/data-table/data-table";
export default function Page() {
return (
<main className="p-6">
<h1 className="mb-4 text-2xl font-semibold">পণ্য</h1>
<DataTable />
</main>
);
}এখন যা বুঝা গেল
useReactTable({ data, columns, getCoreRowModel })— এটাই মূল তিন লাইনgetCoreRowModel()ছাড়া কিছুই কাজ করে না — এটা base layerflexRenderheader ও cell দুটোই রেন্ডার করেheader.isPlaceholder— গ্রুপড কলামের ফাঁকা ঘর ধরতে
পরের অধ্যায়ে দেখবো data ও columns-এর পিছনে আসলে কী লুকিয়ে আছে — অর্থাৎ row, cell, column ইনস্ট্যান্সগুলো কী এবং flexRender-এর context-এ কী থাকে।
useReactTable একটি React hook, তাই ফাইলের উপরে "use client" বা একটা Client Component (data-table.tsx) লাগবেই। Next.js-এ Server Component থেকে সরাসরি ব্যবহার করলে বিল্ড এরর দেবে।