Bỏ qua đến nội dung

Bảng

Một bảng HTML mang tính ngữ nghĩa dành cho hóa đơn, danh sách quản trị và bảng giá — các hàng có viền, tiêu đề in đậm và hiệu ứng bóng theo phong cách tân thô mộc.

import {
  Table,
  TableBody,

Table tạo kiểu cho các phần tử bảng HTML native — <table>, <thead>, <tbody>, <th>, <td>, <caption> — không có thư viện primitive nào bên dưới; biến thể Radix và Base dùng chung một tệp. Công thức neobrutalist ở đây: viền dày và bóng đổ đậm bao quanh cả bảng, ô tiêu đề dùng phông chữ tiêu đề, và vạch chia hàng 2px.

Nên dùng khi:

  • Hóa đơn và đơn hàng — các dòng mặt hàng với một hàng TableFooter cho tổng cộng.
  • Danh sách quản trị — người dùng, khóa API, gói thuê bao, kèm dropdown chứa các hành động theo hàng.
  • Bảng giá và bảng so sánh — ma trận tính năng nơi tiêu đề cột mang ý nghĩa chính.

Cài đặt

pnpm dlx shadcn@latest add https://neobrutalism.com/r/base/table.json
npx shadcn@latest add https://neobrutalism.com/r/base/table.json
yarn dlx shadcn@latest add https://neobrutalism.com/r/base/table.json
bunx --bun shadcn@latest add https://neobrutalism.com/r/base/table.json

Sao chép và dán đoạn mã sau vào dự án của bạn.

components/ui/table.tsx
"use client"

import * as React from "react"

import { cn } from "@/lib/utils"

function Table({ className, ...props }: React.ComponentProps<"table">) {
  return (
    <div
      data-slot="table-container"
      className="relative w-full overflow-x-auto rounded border-2 shadow-md"
    >
      <table
        data-slot="table"
        className={cn("w-full caption-bottom text-sm", className)}
        {...props}
      />
    </div>
  )
}

function TableHeader({ className, ...props }: React.ComponentProps<"thead">) {
  return (
    <thead
      data-slot="table-header"
      className={cn("[&_tr]:border-b-2", className)}
      {...props}
    />
  )
}

function TableBody({ className, ...props }: React.ComponentProps<"tbody">) {
  return (
    <tbody
      data-slot="table-body"
      className={cn("[&_tr:last-child]:border-0", className)}
      {...props}
    />
  )
}

function TableFooter({ className, ...props }: React.ComponentProps<"tfoot">) {
  return (
    <tfoot
      data-slot="table-footer"
      className={cn(
        "border-t-2 bg-muted/50 font-medium [&>tr]:last:border-b-0",
        className
      )}
      {...props}
    />
  )
}

function TableRow({ className, ...props }: React.ComponentProps<"tr">) {
  return (
    <tr
      data-slot="table-row"
      className={cn(
        "border-b-2 transition-colors hover:bg-accent has-aria-expanded:bg-accent data-[state=selected]:bg-accent",
        className
      )}
      {...props}
    />
  )
}

function TableHead({ className, ...props }: React.ComponentProps<"th">) {
  return (
    <th
      data-slot="table-head"
      className={cn(
        "h-10 bg-muted px-2 text-left align-middle font-head font-medium whitespace-nowrap text-foreground [&:has([role=checkbox])]:pr-0",
        className
      )}
      {...props}
    />
  )
}

function TableCell({ className, ...props }: React.ComponentProps<"td">) {
  return (
    <td
      data-slot="table-cell"
      className={cn(
        "p-2 align-middle whitespace-nowrap [&:has([role=checkbox])]:pr-0",
        className
      )}
      {...props}
    />
  )
}

function TableCaption({
  className,
  ...props
}: React.ComponentProps<"caption">) {
  return (
    <caption
      data-slot="table-caption"
      className={cn("mt-4 text-sm text-muted-foreground", className)}
      {...props}
    />
  )
}

export {
  Table,
  TableHeader,
  TableBody,
  TableFooter,
  TableHead,
  TableRow,
  TableCell,
  TableCaption,
}

Cập nhật các đường dẫn import cho phù hợp với cấu hình dự án của bạn.

Cách dùng

import {
  Table,
  TableBody,
  TableCaption,
  TableCell,
  TableHead,
  TableHeader,
  TableRow,
} from "@/components/ui/table"
<Table>
  <TableCaption>A list of your recent invoices.</TableCaption>
  <TableHeader>
    <TableRow>
      <TableHead className="w-[100px]">Invoice</TableHead>
      <TableHead>Status</TableHead>
      <TableHead>Method</TableHead>
      <TableHead className="text-right">Amount</TableHead>
    </TableRow>
  </TableHeader>
  <TableBody>
    <TableRow>
      <TableCell className="font-medium">INV001</TableCell>
      <TableCell>Paid</TableCell>
      <TableCell>Credit Card</TableCell>
      <TableCell className="text-right">$250.00</TableCell>
    </TableRow>
  </TableBody>
</Table>

Kết hợp

Dùng cách kết hợp sau để xây dựng một Table:

Table
├── TableCaption
├── TableHeader
│   └── TableRow
│       ├── TableHead
│       ├── TableHead
│       ├── TableHead
│       └── TableHead
├── TableBody
│   ├── TableRow
│   │   ├── TableCell
│   │   ├── TableCell
│   │   ├── TableCell
│   │   └── TableCell
│   └── TableRow
│       ├── TableCell
│       ├── TableCell
│       ├── TableCell
│       └── TableCell
└── TableFooter

Ví dụ

Chân bảng

Dùng component <TableFooter /> để thêm một chân bảng vào bảng.

import {
  Table,
  TableBody,

Hành động

Một bảng hiển thị các hành động cho từng hàng bằng cách dùng component <DropdownMenu />.

import { MoreHorizontalIcon } from "lucide-react"

import { Button } from "@/components/ui/button"

Data Table

Bạn có thể dùng component <Table /> để xây dựng các bảng dữ liệu phức tạp hơn. Kết hợp nó với @tanstack/react-table để tạo các bảng có sắp xếp, lọc và phân trang.

Xem tài liệu Data Table để biết thêm thông tin.

Bạn cũng có thể xem một ví dụ về bảng dữ liệu trong demo Tasks.

RTL

Để bật hỗ trợ RTL trong Neobrutalism, xem hướng dẫn cấu hình RTL.

"use client"

import * as React from "react"

Khả năng truy cập

Việc render các phần tử bảng native mang lại cho trình đọc màn hình khả năng nhận biết bảng, điều hướng theo hàng/cột và liên kết ô với ô tiêu đề tương ứng mà không cần nối thêm ARIA. Hãy đặt tên cho mỗi bảng bằng TableCaption và chỉ dùng TableHead cho các ô tiêu đề thực sự. Vùng cuộn không focus được, nên người chỉ dùng bàn phím không thể cuộn một bảng bị tràn — nếu nhiều khả năng có bảng rộng, hãy thêm tabIndex={0}, role="region" và một aria-label vào thẻ div chứa trong bản sao thành phần của bạn.