Data Table

Advanced

Bảng dữ liệu với sort, pagination, row selection, expandable rows và server-side pagination.

Giới thiệu

Component bảng dữ liệu mạnh mẽ được xây dựng trên @tanstack/react-table (TanStack Table). Hỗ trợ:

  • Sorting: Sắp xếp cột (client-side)
  • Pagination: Phân trang client-side hoặc server-side
  • Row Selection: Chọn hàng với checkbox
  • Expandable Rows: Mở rộng hàng để xem chi tiết
  • Column Resizing: Kéo đổi kích thước cột
  • Loading State: Hiển thị overlay spinner
  • Empty State: Tự động hiển thị khi không có dữ liệu

Cài đặt

Import component từ @/components/ui/table/Table:

page.tsx

Định nghĩa Column

Sử dụng ColumnDef[] để định nghĩa các cột cho bảng. Mỗi cột bao gồm:

  • accessorKey: Key của dữ liệu trong row
  • header: Tiêu đề cột
  • size: Kích thước cột (auto hoặc fixed)
  • meta: Metadata cho styling (align, className)
  • cell: Custom cell renderer (optional)
columns.tsx

Các props quan trọng trong cell

Hàm cell nhận vào một object CellContext từ TanStack Table. Dưới đây là các props thường dùng nhất:

  • getValue() — Lấy giá trị của cell hiện tại (theo accessorKey).
    Ví dụ: cell: ({ getValue }) => <span>{getValue<string>()}</span>
  • row.original — Truy cập toàn bộ object dữ liệu gốc của hàng. Dùng khi cần lấy nhiều field cùng lúc.
    Ví dụ: cell: ({ row }) => <span>{row.original.firstName} {row.original.lastName}</span>
  • row.index — Chỉ số (index) của hàng trong trang hiện tại (bắt đầu từ 0).
    Ví dụ: cell: ({ row }) => <span>{row.index + 1}</span>
  • row.id — ID duy nhất của hàng do TanStack Table tạo ra (dạng string).
  • renderValue() — Tương tự getValue() nhưng trả về null thay vì undefined khi không có giá trị. An toàn hơn khi render.
  • table — Tham chiếu đến toàn bộ table instance. Dùng để truy cập table.options.meta (custom callbacks như onEdit, onDelete).
    Ví dụ: cell: ({ row, table }) => <Button onClick={() => table.options.meta?.onDelete(row.original)} />
  • column.id — ID của cột, thường là giá trị accessorKey.
Lưu ý: Luôn dùng row.original khi cần truyền data vào action buttons (Edit, Delete, View). Tránh dùng getValue() khi cần nhiều fields vì phải gọi nhiều lần.
columns.tsx

Data Demo

Dữ liệu ví dụ cho bảng:

table.tsx

Basic Table

Bảng đơn giản với sort + pagination client-side mặc định.

Họ tên
Email
Vai trò
Phòng ban
Giao dịch
Trạng thái
Huy Tran
huy@example.com
Owner
Engineering
1.500.000đ
Active
John Doe
john@example.com
Admin
Marketing
850.000đ
Active
Jane Smith
jane@example.com
Editor
Design
320.000đ
Inactive
Bob Wilson
bob@example.com
User
Support
95.000đ
Active
Alice Brown
alice@example.com
User
Sales
200.000đ
Pending
Show 15 of 7 results
1 / 2

Row Selection

Checkbox chọn hàng. Callback onSelectionChange trả về danh sách đã chọn.

Họ tên
Email
Vai trò
Phòng ban
Giao dịch
Trạng thái
Huy Tran
huy@example.com
Owner
Engineering
1.500.000đ
Active
John Doe
john@example.com
Admin
Marketing
850.000đ
Active
Jane Smith
jane@example.com
Editor
Design
320.000đ
Inactive
Bob Wilson
bob@example.com
User
Support
95.000đ
Active
Alice Brown
alice@example.com
User
Sales
200.000đ
Pending
Show 15 of 7 results
1 / 2

Expandable Rows

Click mũi tên để mở rộng hàng, hiển thị thông tin chi tiết.

Họ tên
Email
Vai trò
Phòng ban
Giao dịch
Trạng thái
Huy Tran
huy@example.com
Owner
Engineering
1.500.000đ
Active
John Doe
john@example.com
Admin
Marketing
850.000đ
Active
Jane Smith
jane@example.com
Editor
Design
320.000đ
Inactive
Bob Wilson
bob@example.com
User
Support
95.000đ
Active
Alice Brown
alice@example.com
User
Sales
200.000đ
Pending
Show 15 of 7 results
1 / 2

Column Resizing

Kéo đường viền giữa các cột header để thay đổi kích thước.

Mã SP
Tên sản phẩm
Danh mục
Giá
Tồn kho
Đánh giá
P001
iPhone 15 Pro
Smartphone
29.990.000đ
45
4.8
P002
MacBook Air M3
Laptop
32.990.000đ
12
4.9
P003
iPad Pro 12.9"
Tablet
22.990.000đ
28
4.7
P004
AirPods Pro 2
Audio
6.490.000đ
Hết hàng
4.6
P005
Apple Watch S9
Wearable
11.990.000đ
33
4.5
Show 15 of 7 results
1 / 2

Loading State

isLoading=true hiển thị overlay spinner.

Loading...
Họ tên
Email
Vai trò
Phòng ban
Giao dịch
Trạng thái
Huy Tran
huy@example.com
Owner
Engineering
1.500.000đ
Active
John Doe
john@example.com
Admin
Marketing
850.000đ
Active
Jane Smith
jane@example.com
Editor
Design
320.000đ
Inactive
Bob Wilson
bob@example.com
User
Support
95.000đ
Active
Alice Brown
alice@example.com
User
Sales
200.000đ
Pending
Show 15 of 7 results
1 / 2

Empty State

data=[] tự động hiển thị trạng thái trống.

Họ tên
Email
Vai trò
Phòng ban
Giao dịch
Trạng thái
No data

Server Pagination

Truyền total từ BE → tự động bật server mode. onChange nhận (page, pageSize) 1-based để gọi API.

Tổng: 0 người dùng

Họ tên
Email
Vai trò
Phòng ban
Giao dịch
Trạng thái
No data

Tùy biến Pagination

Tùy chỉnh showTotal, pageSizeOptions, showSizeChanger=false để ẩn size selector.

Họ tên
Email
Vai trò
Phòng ban
Giao dịch
Trạng thái
Huy Tran
huy@example.com
Owner
Engineering
1.500.000đ
Active
John Doe
john@example.com
Admin
Marketing
850.000đ
Active
Jane Smith
jane@example.com
Editor
Design
320.000đ
Inactive
13 / 7 kết quả
1 / 3

Props & API

TableProps

PropTypeDefaultMô tả
data*TData[]Mảng dữ liệu hiển thị trong bảng
columns*ColumnDef<TData, TValue>[]Định nghĩa các cột theo chuẩn @tanstack/react-table
isLoadingbooleanfalseHiển thị loading overlay spinner khi đang tải dữ liệu
enableSortingbooleantrueBật/tắt tính năng sắp xếp theo cột
enableRowSelectionbooleanfalseBật checkbox chọn từng hàng và chọn tất cả
enableExpandingbooleanfalseBật nút mở rộng hàng để hiện sub-component
renderSubComponent(props: { row: TData }) => ReactNodeRender nội dung khi hàng được mở rộng (dùng kèm enableExpanding)
getRowCanExpand(row: TData) => booleanKiểm soát hàng nào được phép mở rộng
onSelectionChange(selectedRows: TData[]) => voidCallback trả về danh sách hàng đang được chọn
enableColumnResizingbooleanfalseBật kéo thả để thay đổi độ rộng cột
columnResizeMode"onChange" | "onEnd""onChange"Chế độ resize: onChange (realtime) hoặc onEnd (khi thả chuột)
paginationPaginationConfig | false{}Cấu hình phân trang. false = tắt. Có total → server mode tự động
labelsTableLabelsTuỳ chỉnh chuỗi UI: page, perPage, empty
virtualizebooleanfalseBật row virtualization cho dataset lớn (bỏ qua pagination khi bật)
virtualHeightnumber400Chiều cao (px) của vùng scroll khi virtualize = true
estimatedRowHeightnumber45Chiều cao ước tính mỗi hàng (px) cho virtualizer
classNamestringClass tuỳ chỉnh cho wrapper ngoài cùng

PaginationConfig

PropTypeDefaultMô tả
currentnumberTrang hiện tại (1-based). Dùng để controlled ở server mode
pageSizenumber10Số dòng hiển thị mỗi trang
totalnumberTổng số bản ghi từ BE. Có giá trị → tự động bật server mode
pageSizeOptionsnumber[][5,10,20,50,100]Danh sách lựa chọn số dòng/trang
showTotal(total, range) => ReactNodeRender tùy chỉnh info tổng số kết quả
showSizeChangerbooleantrueHiện/ẩn dropdown chọn số dòng/trang
onChange(page: number, pageSize: number) => voidCallback khi đổi trang hoặc pageSize. Bắt buộc ở server mode