Skip to content
v2.5.0
English
Русский
Documentation

Table

Table is a compound component for semantic tabular data where native table structure matters more than grid-like keyboard interaction.
PlanPer month
Starter$9
Team$29
table.tsx · 18 lines
import {
  Table,
  TableBody,
  TableData,
  TableHead,
  TableHeading,
  TableRow,
} from '@vega-ui/react';

<Table fullWidth>
  <TableHead>
    <TableRow><TableHeading>Plan</TableHeading><TableHeading>Per month</TableHeading></TableRow>
Browser support
Widely available
  • Chrome 111
  • Edge 111
  • Firefox 113
  • Safari 15.4
  • Safari on iOS 15.4
Bundle size
Tested in
25 tests

Summary

Table is a compound component for semantic tabular data where native table structure matters more than grid-like keyboard interaction.

Imports

import {
  Table,
  TableHead,
  TableBody,
  TableRow,
  TableHeading,
  TableData,
  TableFoot,
  type TableProps,
} from '@vega-ui/react';

Exported Types

  • TableProps
  • TableHeadProps
  • TableBodyProps
  • TableRowProps
  • TableHeadingProps
  • TableDataProps
  • TableFootProps

Minimal Composition

minimal-composition.tsx · 31 lines
import {
  Table,
  TableBody,
  TableData,
  TableHead,
  TableHeading,
  TableRow,
} from '@vega-ui/react';

export default function MinimalComposition() {
  return (
    <Table>

Required Parts

  • Table: root semantic table and alignment owner
  • TableRow: row structure for sections
  • TableHeading or TableData: cell-level content

Optional Parts

  • TableHead: header section for column labels
  • TableBody: body section for data rows
  • TableFoot: footer section for summaries or totals

Composition Order

  1. Table
  2. TableHead
  3. TableBody
  4. TableFoot
  5. TableRow
  6. TableHeading
  7. TableData

Variants

  • Data alignment through dataAlign
  • fullWidth and fullHeight layout variants
  • Edge-padded versus flush edge treatment through edgePadded

Edge Cases

  • Table is a poor fit when the UI needs cell-level keyboard navigation or complex selection models.
  • Small screens need explicit overflow or stacked-layout treatment in the consumer.
  • Alignment choices should stay consistent across heading and data cells.

Common Mistakes

  • Reaching for DataGrid when semantic table markup is enough.
  • Dropping semantic sections like TableHead just to simplify styling.
  • Ignoring overflow behavior on smaller screens.

Examples

Basic

Basic: plan comparison with row headers

A caption names the table, column headings use scope='col' and the first cell of every row is a TableHeading with scope='row', so a screen reader announces “Single sign-on, Team” for each value. Check and dash icons are hidden and paired with visually hidden “Included” / “Not included” text.

plan-comparison-table.tsx · 60 lines
import { Check, Minus } from '@vega-ui/icons';
import {
  Icon,
  Table,
  TableBody,
  TableData,
  TableHead,
  TableHeading,
  TableRow,
  VisuallyHidden,
} from '@vega-ui/react';

Basic: line items with computed totals

Subtotal, tax and total are computed from the line items and placed in TableFoot. The labels are row headings spanning two columns, so each total stays tied to its label.

summary-table-with-footer-totals.tsx · 59 lines
import {
  Table,
  TableBody,
  TableData,
  TableFoot,
  TableHead,
  TableHeading,
  TableRow,
} from '@vega-ui/react';

const lines = [
  { item: 'Team plan · 12 seats', quantity: 12, unitPrice: 29 },

Controlled/Stateful

Controlled/Stateful: sortable columns

The sort column and direction live in state; each sortable heading contains a real button and exposes the current order with aria-sort. Amounts sort numerically and dates by their ISO value, while the cells show formatted text.

sortable-columns.tsx · 96 lines
import { ArrowDown, ArrowUp, ArrowUpDown } from '@vega-ui/icons';
import {
  Button,
  Icon,
  Table,
  TableBody,
  TableData,
  TableHead,
  TableHeading,
  TableRow,
} from '@vega-ui/react';
import { useState } from 'react';

Form/Integration

Form/Integration: selecting rows for a bulk action

Every row has a named checkbox (name='member', value = row id), and the header checkbox selects all rows or shows the indeterminate state for a partial selection. On submit the handler collects the selected ids with FormData.getAll.

row-selection-form.tsx · 99 lines
import {
  Button,
  Checkbox,
  CheckboxCheckedIcon,
  CheckboxIndeterminateIcon,
  CheckboxIndicator,
  CheckboxInput,
  HelperText,
  Table,
  TableBody,
  TableData,
  TableHead,

Layout/Overlay

Layout/Overlay: scrollable table inside a dialog

A long list of charges inside a dialog scrolls in its own focusable region instead of stretching the dialog. The header cells are sticky and get the dialog surface color, so rows do not show through them.

billing-table-inside-a-dialog.tsx · 71 lines
import {
  Button,
  Dialog,
  DialogClose,
  DialogContent,
  DialogTrigger,
  Heading,
  Icon,
  IconButton,
  Table,
  TableBody,
  TableData,

Error

Error: rows failed to load

Loading, error and data states share the same table: the loading and error rows span every column via colSpan={columns.length}. The error is announced with role='alert' and Retry loads the rows on the second attempt.

failed-to-load-rows.tsx · 78 lines
import {
  Button,
  HelperText,
  Spinner,
  Table,
  TableBody,
  TableData,
  TableHead,
  TableHeading,
  TableRow,
} from '@vega-ui/react';
import { useEffect, useState } from 'react';

Disabled

Disabled: archived rows with locked actions

Archived rows use a secondary text color and an “Archived” badge, so the state is not conveyed by color alone, and their row actions are disabled. Each action button has an accessible name that includes its project.

visually-subdued-archived-rows.tsx · 53 lines
import {
  Badge,
  Button,
  Table,
  TableBody,
  TableData,
  TableHead,
  TableHeading,
  TableRow,
} from '@vega-ui/react';

const projects = [

Edge

Edge: empty result after filtering

When the search matches nothing, the body renders one row spanning all columns with an explanation and a way out. The headers stay in place, so the table does not collapse.

empty-state-row-when-data-is-unavailable.tsx · 68 lines
import {
  Button,
  Label,
  Table,
  TableBody,
  TableData,
  TableHead,
  TableHeading,
  TableRow,
  Text,
  TextField,
  TextFieldInput,

Edge: 200 rows and 8 columns in a scroll container

The table keeps its natural width and scrolls in both directions inside a role='region' with tabIndex={0}, so keyboard users can scroll it too. The header row sticks to the top while scrolling.

scrollable-wide-table.tsx · 58 lines
import {
  Table,
  TableBody,
  TableData,
  TableHead,
  TableHeading,
  TableRow,
  Text,
} from '@vega-ui/react';
import { CSSProperties, useId } from 'react';

const columns = ['Order', 'Customer', 'City', 'Items', 'Subtotal', 'Shipping', 'Status', 'Placed'];

Edge: long unbreakable text

With table-layout: fixed and column widths in a colgroup, a long file name wraps anywhere (overflow-wrap: anywhere), while an email is truncated with an ellipsis and keeps the full value in title. Short columns never get squeezed.

long-cell-content.tsx · 48 lines
import {
  Table,
  TableBody,
  TableData,
  TableHead,
  TableHeading,
  TableRow,
} from '@vega-ui/react';

const files = [
  { name: 'q3-board-presentation-final-v7-reviewed-by-legal-and-finance-DO-NOT-SHARE.pdf', owner: 'maya.chen@northwind-traders-international.example.com', size: '18.4 MB' },
  { name: 'logo.svg', owner: 'li@acme.io', size: '4 KB' },

Edge: flush edges inside a card

By default edgePadded is false: the first and last cells lose their outer padding so the text lines up with the card heading. Toggle the prop to compare it with padded edges.

flush-edges-without-extra-padding.tsx · 41 lines
import {
  Button,
  Card,
  Heading,
  Table,
  TableBody,
  TableData,
  TableRow,
} from '@vega-ui/react';
import { useState } from 'react';

const details = [

Anatomy

Overview

Table owns shared alignment and edge-padding context for a semantic table structure. It is a compound component because root and section parts cooperate through one data-display contract.

Required Parts

Table

Required. Owns alignment, edge padding, and full-width or full-height layout flags.

TableRow

Required. Defines row structure inside header, body, or footer sections.

TableHeading or TableData

Required. Provide the actual semantic cells for a row.

Optional Parts

TableHead

Recommended for column labels and accessible column structure.

TableBody

Recommended for data rows.

TableFoot

Optional footer for totals, summaries, or aggregated values.

Composition Order

  1. Table
  2. TableHead
  3. TableBody
  4. TableFoot
  5. TableRow
  6. TableHeading
  7. TableData

Valid Composition Patterns

summary-table-with-footer-totals.tsx · 59 lines
import {
  Table,
  TableBody,
  TableData,
  TableFoot,
  TableHead,
  TableHeading,
  TableRow,
} from '@vega-ui/react';

const lines = [
  { item: 'Team plan · 12 seats', quantity: 12, unitPrice: 29 },

Invalid Composition Patterns

Visual table layout without semantic table parts

This weakens native table meaning and often hurts accessibility.

Mixing heading and data roles arbitrarily

Column labels should stay in TableHeading rather than becoming styled data cells.

Treating Table like an interactive data grid

If the feature needs cell navigation, selection models, or richer grid behavior, choose DataGrid instead.