Data Table
AdvancedBả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:
Đị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)
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ềnullthay vìundefinedkhi 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.
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.Data Demo
Dữ liệu ví dụ cho bảng:
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 |
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 |
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 |
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 |
Loading State
isLoading=true hiển thị overlay spinner.
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 |
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 |
Props & API
TableProps
| Prop | Type | Default | Mô 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 |
isLoading | boolean | false | Hiển thị loading overlay spinner khi đang tải dữ liệu |
enableSorting | boolean | true | Bật/tắt tính năng sắp xếp theo cột |
enableRowSelection | boolean | false | Bật checkbox chọn từng hàng và chọn tất cả |
enableExpanding | boolean | false | Bật nút mở rộng hàng để hiện sub-component |
renderSubComponent | (props: { row: TData }) => ReactNode | — | Render nội dung khi hàng được mở rộng (dùng kèm enableExpanding) |
getRowCanExpand | (row: TData) => boolean | — | Kiểm soát hàng nào được phép mở rộng |
onSelectionChange | (selectedRows: TData[]) => void | — | Callback trả về danh sách hàng đang được chọn |
enableColumnResizing | boolean | false | Bậ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) |
pagination | PaginationConfig | false | {} | Cấu hình phân trang. false = tắt. Có total → server mode tự động |
labels | TableLabels | — | Tuỳ chỉnh chuỗi UI: page, perPage, empty |
virtualize | boolean | false | Bật row virtualization cho dataset lớn (bỏ qua pagination khi bật) |
virtualHeight | number | 400 | Chiều cao (px) của vùng scroll khi virtualize = true |
estimatedRowHeight | number | 45 | Chiều cao ước tính mỗi hàng (px) cho virtualizer |
className | string | — | Class tuỳ chỉnh cho wrapper ngoài cùng |
PaginationConfig
| Prop | Type | Default | Mô tả |
|---|---|---|---|
current | number | — | Trang hiện tại (1-based). Dùng để controlled ở server mode |
pageSize | number | 10 | Số dòng hiển thị mỗi trang |
total | number | — | Tổng số bản ghi từ BE. Có giá trị → tự động bật server mode |
pageSizeOptions | number[] | [5,10,20,50,100] | Danh sách lựa chọn số dòng/trang |
showTotal | (total, range) => ReactNode | — | Render tùy chỉnh info tổng số kết quả |
showSizeChanger | boolean | true | Hiện/ẩn dropdown chọn số dòng/trang |
onChange | (page: number, pageSize: number) => void | — | Callback khi đổi trang hoặc pageSize. Bắt buộc ở server mode |