Tech TutorBangla Web Documentation
Portfolio
HomeHTMLCSSTailwind CSSJavaScriptTypeScriptReactNext.jsNode.jsExpressSocket.IOExpoWordPressPostgreSQLPrismaPostgresNestJSMongoDBDockerTanStackTanStack TableRTK QueryGitGitHub
  • TanStack Table Documentation12টি অধ্যায়
প্রযুক্তি নির্বাচন
HomeHTMLCSSTailwind CSSJavaScriptTypeScriptReactNext.jsNode.jsExpressSocket.IOExpoWordPressPostgreSQLPrismaPostgresNestJSMongoDBDockerTanStackTanStack TableRTK QueryGitGitHub
  • Introduction
  • Installation & Setup
  • Core Concepts
  • Column Definitions
  • Row Models
  • Sorting
  • Filtering
  • Pagination
  • Row Selection
  • Column Management
  • Server-side Mode
  • Advanced Features
  1. Docs
  2. TanStack Table
  3. ColumnDef — কলাম সংজ্ঞা
TanStack TableIntermediate

ColumnDef — কলাম সংজ্ঞা

accessorKey বনাম accessorFn বনাম id, header/cell রেন্ডার, display column, enable* অপশন, meta, columnHelper — বাংলায়।

30টি অংশবাংলা ভাষায়

টেবিলের প্রতিটি কলাম একটি ColumnDef অবজেক্ট। এই একটি অবজেক্টই বলে দেয় কোন ডেটা দেখাবে, কীভাবে format করবে, আর কোন কাজগুলো চালু থাকবে।

তিনটি উপায়ে ডেটা ঠিক করা

columns.tsxtypescript
typescript
import type { ColumnDef } from "@tanstack/react-table";

const columns: ColumnDef<Product>[] = [
  // ১. accessorKey — ডেটার key-র নামই id
  { accessorKey: "name", header: "নাম" },

  // ২. accessorFn — হিসাব করে মান বানানো
  {
    id: "fullName",
    accessorFn: (row) => `${row.name} (${row.stock}টি)`,
    header: "বিবরণ",
  },

  // ৩. display column — ডেটা নেই, শুধু UI
  {
    id: "actions",
    header: "অ্যাকশন",
    cell: ({ row }) => <RowActions id={row.original.id} />,
  },
];
উপায়id কোথা থেকেকখন ব্যবহার
accessorKeykey-র নাম নিজে থেকেইসরাসরি একটা ফিল্ড দেখালে
accessorFnid আলাদা লিখতে হয়জোড়া লাগা বা গণনা করা ডেটা
id + cellid আলগা করেবাটন, checkbox, badge

id ছাড়া শুধু accessorFn লিখলে কম্পাইলার এরর দেবে — id বলে দিতে হয় কোন কলামের কথা বলছেন।

id বদলালে সব ভাঙে

কলামের id = sorting/filtering state-এর key, URL-এর query param, আর getRowId-এর মতোই। তাই id বা accessorKey বদলানোর পর লুকানো সাজানো পুরোোনো URL ভেঙে যেতে পারে। স্থায়ী রাখুন।

header — হেডার লেখা ও action

// সহজ: শুধু লেখা
{ accessorKey: "name", header: "পণ্যের নাম" }

// header-এ sorting button — সবচেয়ে common
{
  accessorKey: "price",
  header: ({ column }) => (
    <Button
      variant="ghost"
      onClick={() => column.toggleSorting(column.getIsSorted() === "asc")}
    >
      মূল্য
      {column.getIsSorted() === "asc" && <ArrowUp className="ml-2 size-4" />}
      {column.getIsSorted() === "desc" && <ArrowDown className="ml-2 size-4" />}
    </Button>
  ),
}

// align করা
{
  accessorKey: "price",
  header: "মূল্য",
  meta: { align: "right" },
}

header-এর function-এ column instance পাওয়া যায়, তাই getIsSorted() দিয়ে তীর দেখাতে পারেন। meta.align নিজের নিয়ম — TanStack এটা চেনে না, রেন্ডারে পড়ে class বানাতে হয় (নিচে দেখানো হলো)।

meta ব্যবহারtsx
tsx
// meta পড়া — মূল রেন্ডারে
<TableHead className={cn(
  "px-4",
  header.column.columnDef.meta?.align === "right" && "text-right",
)}>

cell — মান render করা

// accessorKey + cell একসাথে
{
  accessorKey: "price",
  header: "মূল্য",
  cell: ({ getValue }) => {
    const price = getValue<number>(); // ← টাইপ জানানো
    return (
      <span className="font-medium tabular-nums">
        {new Intl.NumberFormat("bn-BD", {
          style: "currency",
          currency: "BDT",
          maximumFractionDigits: 0,
        }).format(price)}
      </span>
    );
  },
}

// অন্য কলামের ডেটা দরকার হলে
{
  id: "status",
  accessorFn: (row) => (row.stock === 0 ? "out" : "in"),
  cell: ({ row, getValue }) => (
    <Badge variant={getValue() === "out" ? "destructive" : "default"}>
      {getValue() === "out" ? "স্টক শেষ" : row.original.name}
    </Badge>
  ),
}
getValue কী দেয়, জানেন তা?

cell function-এর getValue() সবসময় এই কলামের accessor-এর ফলাখানা। যদি accessorKey না থাকে (display column), তাহলে getValue() undefined দেয় — সেখানে row.original থেকে নিন।

নিজস্ব cell component

বড় টেবিলে cell function-এর ভেতরে লজিক রাখলে columns.tsx পড়া কঠিন হয়ে যায়। আলাদা ফাইলে component লিখে রেখে সেটা রেফার করুন।

// components/data-table/cell/price-cell.tsx
export function PriceCell({ value }: { value: number }) {
  const format = new Intl.NumberFormat("bn-BD", {
    style: "currency",
    currency: "BDT",
    maximumFractionDigits: 0,
  });

  return (
    <span className="font-medium tabular-nums">{format.format(value)}</span>
  );
}

// columns.tsx
{ accessorKey: "price", cell: ({ getValue }) => <PriceCell value={getValue<number>()} /> }

enable* — কোন কাজ চালু থাকবে

const columns: ColumnDef<Product>[] = [
  {
    accessorKey: "name",
    enableSorting: false,        // এ কলাম সাজানো যাবে না
    enableHiding: false,          // column visibility মেনুতে আসবে না
    enableGlobalFilter: false,   // global search-এ অংশ নেবে না
  },
  {
    id: "select",
    enableSorting: false,
    enableHiding: false,
    cell: ({ row }) => <Checkbox />,
  },
  {
    accessorKey: "createdAt",
    enableGlobalFilter: false,   // ISO date global search-এ noise দেয়
  },
];
optiondefaultfalse করলে
enableSortingtrueheader-এ সাজানোর তীর আসবে না
enableHidingtruecolumn visibility তালিকায় থাকবে না
enableGlobalFiltertrueglobal search এ কলামটা মিলবে না
enableGroupingtruegrouping করা যাবে না
enablePinningtruepin/unpin করা যাবে না

columnHelper — boilerplate কমানো

createColumnHelper দিলে accessor-এর নাম থেকেই টাইপ বের হয়ে আসে — আলাদা করে <number> লেখা লাগে না। বড় প্রজেক্টে এটা ব্যবহার করা ভালো।

columnHelper দিয়েtypescript
typescript
import { createColumnHelper } from "@tanstack/react-table";

const helper = createColumnHelper<Product>();

export const columns = [
  helper.accessor("name", {
    header: "পণ্যের নাম",
  }),
  helper.accessor("price", {
    header: "মূল্য",
    cell: (info) => <PriceCell value={info.getValue()} />,
    //                         ↑ info.getValue(): number — টাইপ ইনফার
  }),
  helper.display({
    id: "actions",
    header: "অ্যাকশন",
    cell: ({ row }) => <RowActions id={row.original.id} />,
  }),
];
সাধারণ ColumnDefcolumnHelper
accessorKey: "price"helper.accessor("price", …)
id: "x" + accessorFnhelper.accessor("x", …)
id: "y" + cellhelper.display({ id: "y", … })
getValue<number>()info.getValue()

একটা সম্পূর্ণ columns.tsx

components/data-table/columns.tsxtsx
tsx
import { ArrowDown, ArrowUp, ChevronsUpDown } from "lucide-react";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import type { ColumnDef } from "@tanstack/react-table";

import { PriceCell } from "./cell/price-cell";
import { RowActions } from "./cell/actions-cell";

const formatDate = new Intl.DateTimeFormat("bn-BD", {
  dateStyle: "medium",
});

export const columns: ColumnDef<Product>[] = [
  {
    accessorKey: "name",
    header: ({ column }) => (
      <Button
        variant="ghost"
        onClick={() => column.toggleSorting(column.getIsSorted() === "asc")}
      >
        পণ্যের নাম
        {column.getIsSorted() === "asc" && <ArrowUp className="ml-2 size-4" />}
        {column.getIsSorted() === "desc" && <ArrowDown className="ml-2 size-4" />}
        {!column.getIsSorted() && <ChevronsUpDown className="ml-2 size-4 opacity-50" />}
      </Button>
    ),
    cell: ({ row }) => (
      <div className="flex flex-col">
        <span className="font-medium">{row.original.name}</span>
        <span className="text-xs text-muted-foreground">
          {row.original.id}
        </span>
      </div>
    ),
  },
  {
    accessorKey: "price",
    header: ({ column }) => (
      <Button
        variant="ghost"
        className="-ml-3"
        onClick={() => column.toggleSorting(column.getIsSorted() === "asc")}
      >
        মূল্য
        {column.getIsSorted() === "asc" && <ArrowUp className="ml-2 size-4" />}
        {column.getIsSorted() === "desc" && <ArrowDown className="ml-2 size-4" />}
      </Button>
    ),
    cell: ({ getValue }) => <PriceCell value={getValue<number>()} />,
    meta: { align: "right" },
  },
  {
    accessorKey: "stock",
    header: "স্টক",
    cell: ({ getValue }) => {
      const stock = getValue<number>();
      return (
        <Badge variant={stock === 0 ? "destructive" : "secondary"}>
          {stock === 0 ? "শেষ" : `${stock}টি`}
        </Badge>
      );
    },
  },
  {
    accessorKey: "createdAt",
    header: "তারিখ",
    enableGlobalFilter: false,
    cell: ({ getValue }) => (
      <span className="text-muted-foreground">
        {formatDate.format(new Date(getValue<string>()))}
      </span>
    ),
  },
  {
    id: "actions",
    header: "অ্যাকশন",
    enableSorting: false,
    enableHiding: false,
    cell: ({ row }) => <RowActions id={row.original.id} />,
    meta: { align: "right" },
  },
];
actions কলামে enable দুটো বন্ধ

checkbox আর actions-এর মতো display column-এ enableSorting: false আর enableHiding: false দুটোই দিন। না দিলে header-এ অর্থহীন সাজানোর তীর চড়ে উঠবে, আর column visibility মেনুতে "actions" নামে একটা মূর্খ item থাকবে।

সাধারণ ভুল

ভুলফলসমাধান
accessorFn ছাড়া শুধু cellsorting কাজ করে নাaccessorKey বা accessorFn দিন
getValue()-কে + 1"1850001" হয়Number(getValue())
header: () => "নাম"context পেলেও কোচাকাচাheader: "নাম"
অ্যাকশন কলাম enableHiding: false ছাড়ামেনুতে ভুল itemদুটোই false
প্রতিটি রেন্ডারে new Intl.NumberFormat()বারবার অবজেক্ট তৈরিফাইলের উপরে একবার

পরবর্তী

এখন কলাম ঠিক আছে, কিন্তু ডেটা কীভাবে ভেতরে ঢোকে সেটা বোঝা বাকি। পরের অধ্যায়ে row model-এর pipeline দেখবো — core, filtered, sorted, grouped, expanded, paginated।

শেষ প্যারায় নিজে practice করুন — কোড কপি করে নিজের এডিটরে চালিয়ে দেখুন।
আগেরমূল ধারণা — useReactTable ও row/cell/columnপরেরRow Model — ডেটার পাইপলাইন
এই পেজে
Tech Tutor

বাংলায় ওয়েব ডেভেলপমেন্ট ডকুমেন্টেশন শিখুন — ধাপে ধাপে।

Homeসব ডকুমেন্টেশনQuick Startআমার নোটস

© 2026 Tech Tutor. বাংলায় শিখুন, বাংলায় গড়ুন।

mdanamolhasan1998@gmail.com