Table

Table is a container for displaying information from a data set. It allows users to quickly scan, sort, compare, and take action on large amounts of data.

Import#

import { Table } from '@volue/wave-react';

Table is a compound component that consists of multiple parts which can be composed together to achieve the desired table interface:

  • ​
    Table: The wrapper that contains all the parts of a table.
  • ​
    Table.Head: The container that defines the head of the table's columns (equivalent of <thead> HTML element).
  • ​
    Table.Header: A convenience container wrapping content into Table.Head with single Table.Row inside.
  • ​
    Table.ColumnHeader: Renders the header cell of a column (equivalent of native <th> element). Wraps text content into Table.TableColumnHeaderContent and optionally shows Table.SortIndicator.
  • ​
    Table.ColumnHeaderContent: Wrapper for column's header text contents.
  • ​
    Table.Body: The container grouping a set of Table.Rows that are main content of the table (equivalent of native <tbody> element).
  • ​
    Table.Row: Renders table row (equivalent of native <tr> element).
  • ​
    Table.Cell: Cell to be placed in the Table.Row that displays single piece of data in the column (equivalent of native <td> element). Wraps text content into Table.CellContent automatically.
  • ​
    Table.CellContent: Wrapper for cell's text contents.
  • ​
    Table.SelectionCell: Composes Table.Cell and Table.SelectionCellCheckbox, to render a cell with checkbox for implementing selection of rows.
  • ​
    Table.SelectionCellCheckbox: Customized Checkbox component to be placed in the table cell.
  • ​
    Table.Foot: The container grouping a set of Table.Rows which is placed at the bottom of the table (equivalent of native <tfoot> element).
  • ​
    Table.Footer: A convenience container wrapping content into Table.Foot with single Table.Row inside.
  • ​
    Table.EmptyState: The wrapper for providing additional context when the table data is either unavailable or nonexistent.

Examples#

Basic#

If you need to display basic key and value pairs, consider Data List instead.

Table appearance#

The root Table component accepts following properties to infuence the appearance of every cell:

  • ​
    cellSize — controls the size of each cell.
  • ​
    alignY — controls vertical alignment of content inside cells.
  • ​
    overflowStrategy — wraps or truncate content inside a cell.
  • ​
    highlightOnHover — controls whether rows within Table.Body should be highlighted on hover.
  • ​
    rowSeparation - controls the appearance of row separation.

Outer outline of the Table can be disabled by setting outlined property to false:

Cell appearance#

The Table.Cell component accepts following props to adjust appearance:

  • ​
    align — controls the horizontal alignment of cell contents.
  • ​
    maxWidth — sets the maximum width of the cell.
  • ​
    minWidth — sets the minimum width of the cell.
  • ​
    width — sets the width of the cell.

Since the table layout is created with CSS Flexbox​, all cells have evenly distributed widths by default.

Rows selection#

Use Table.SelectionCell to implement selection of rows.

Table overflow#

When there isn’t enough space to fit all the rows or columns within table's container, it becomes scrollable.

You can apply sticky positioning to Table.Header and Table.Footer along the vertical axis via the stickyOffset property:

  • ​
    the header will be offset against the top edge.
  • ​
    the footer will be offset against the bottom edge.

Table.ColumnHeader and Table.Cell can be "sticky" along the horizontal axis:

  • ​
    positive values (e.g. 0) will offset the cell against the left edge.
  • ​
    negative values (e.g. -0) will offset the cell against the right edge.

When stickyOffset is provided, the showDivider property will default to true.

Synced scroll#

By default, stickyOffset is relative to nearest scrolling ancestor (the Table itself). If the table doesn't scroll vertically within the overflowing container and you want the Table.Header or Table.Footer to stick while scrolling on the document window, use syncScroll property.

When syncScroll is enabled, table's row groups become scrolling containers instead of the Table and their scrolling position is synced up.

More details about this technique are available in the following article: https://uxdesign.cc/position-stuck-96c9f55d9526​

Custom cell content#

You may provide any custom content to Table.ColumnHeader and Table.Cell.

Note that when providing ReactText (string and number) content to Table.Cell, the cells will benefit from improved re-render performance thanks to React.memo.

Each individual table cell can support multiple rows of text. This is useful when you want to provide related details in a table without adding additional columns, such as placing an employee's email address beneath their name.

Row actions#

Row actions can be represented as icon buttons. When there is only one action available, you may provide a tooltip for better clarity.

When multiple actions are available on the row, use Menu component for displaying list of actions.

In some cases, you may want to attach an action to an entire row.

Highlighting rows#

You can highlight individual rows with isHighlighted property. Highlights provide additional visual prominence to a row and is useful to quickly differentiate it from other rows.

Keep in mind that users with visual impairments may not notice when rows are highlighted, so prefer not to rely on highlights alone to convey information.

Never use highlighted rows to indicate that a user has selected the row. Refer to rows selection example instead.

Row tones#

Table.Row supports setting tone property.

Tones are typicaly used to emphasize the status of a row and faciliate users in identyfing which parts of the table require attention.

Supported tones include:

  • ​
    danger - for statuses that imply error, failure or problem.
  • ​
    success - for statuses that are positive, e.g. confirmations.
  • ​
    warning - for statuses that are in-progress, pending, or that could require user intervention.

Be sure to utilize tones purposefully to convey important statuses. Excessive use of tones can lead to noisy interface with too many elements pulling user attention.

Row tones should not be used alone as the only indicator of status. Be sure to include actual status labels in the table itself when possible.

Note that you can provide a custom tone by using tone="custom" property and assigning specific color to --table-cell-bg-color CSS variable.

Cell tones#

Similar to row tones, a single cell also supports setting tone property. This can be useful to grab a user’s attention on a specific cell(s) in the table.

Be sure to utilize tones purposefully to convey important statuses. Overuse of tones can dilute attention on what matters most.

When using Table with cell tones, you may want to visually separate elements using showCellSeparation property to enhance visual structure of the table.

Loading state#

Rather than using a Spinner component, you can use the Skeleton placeholders in the cells to create a more pleasant loading experience.

Empty state#

When there is no data to feed the table or no match can be found while filtering, render Table.EmptyState instead of Table.Body to provide additional context for the user.

For example, if a table of users isn’t populated yet, the empty state within the table shows the user what data to expect and optionally offers an action to populate the table.

Table inside a card#

The table can be contextually styled to better fit the container it is placed into, such as Card.

Use addHorizontalWhitespace property to add additional horizontal padding on the outer table cells equal to the card padding (or equal to the whitespace between adjacent cells when placed outside a card).

Overflowing cell content#

You may use useHasOverflow hook to dynamically apply Tooltip only when content overflows Table.CellContent area. The tooltip will show full content when hovered over the abbreviated cell text.

Try manipulating cell widths or wrap long content into multiple lines with overflowStrategy set to wrap to prevent text content overflowing the cell.

Drag and drop#

With libraries such as react-dnd you can easily implement drag-and-drop feature over table rows and change their order.

Don't forget to wrap your App into DndProvider with appropriate backend value.

Check react-dnd docs​ for more information.

import { DndProvider } from 'react-dnd';
import { HTML5Backend } from 'react-dnd-html5-backend';
function App() {
return (
<DndProvider backend={HTML5Backend}>
<Main />
</DndProvider>
);
}

react-dnd provides a way to separate dragRef and dropRef to use them on different DOM elements. It can be useful when there are multiple actionable elements in table rows.

dragRef and dropRef can be merged into one ref which can be useful to make whole rows draggable.


Integration with TanStack Table#

Table component consists of lightweight, "primitive" buliding blocks designed for composability. Primitives are low-level components that assume as little as possible about their usage and they don't own any complex state.

This works well for simple or static tables, but there are instances where functionality such as sorting, search, filtering, virtualization​ or pagination are required. This kind of functionality is out of scope for the Table component, as the spectrum of possible use-cases is too broad for a single component to cover effectively. We aim to avoid monolithic components with a lot of assumptions about how you use them and increased API surface area, i.e. endless prop configuration.

Whilst the Table component is limited out of the box, these same limitations make it very easy to integrate with third-party libraries which offer advanced functionality. One such library that we recommend is TanStack Table​. It is a headless UI library​, which supplies you with functions, state and utilities to add more advanced functionality and interactions to your tables. It does not bring any user interface or styles itself, making it a perfect extension for Wave's Table.

Examples below show how to combine TanStack Table​ with Wave's Table.

To use TanStack Table features, you should install @tanstack/react-table package in your project.

npm install @tanstack/react-table --save
# or
yarn add @tanstack/react-table

Basic#

import {
flexRender,
getCoreRowModel,
useReactTable,
createColumnHelper
} from '@tanstack/react-table';

Sorting columns#

Use TanStack Table Sorting API​ together with onClick and sort properties on Table.ColumnHeader to render the UI for a sort indicator and allow the user to change the sort order of the data.

You should only use client-side sorting if your data set is small and exists in-memory, otherwise sorting should be implemented by sorting in the actual database.

Custom sorting icon can be provided with sortIndicator attribute. Check different icon by sorting Name column below.

Table data can be sorted by multiple columns by clicking on a column header while holding down the shift key. This is controlled with the enableMultiSort property​.

import {
flexRender,
getCoreRowModel,
getSortedRowModel,
useReactTable,
createColumnHelper
} from '@tanstack/react-table';

Filtering#

Filtering data can be implemented with TanStack Table Filters API​. Table data can be filtered globally with use of globalFilter prop or it can be column filtered with columnFilters.

import {
flexRender,
getCoreRowModel,
getFilteredRowModel,
useReactTable,
createColumnHelper
} from '@tanstack/react-table';

Selecting rows#

Rows selection can be implemented with Row Selection API​. In the example below batch actions will be shown upon rows selection.

import {
flexRender,
getCoreRowModel,
useReactTable,
createColumnHelper
} from '@tanstack/react-table';

Pagination#

With help of Pagination component to display the pagination UI and TanStack Pagination API​, larger table data sets can be split into pages.

Often pagination is performed server-side and Table already receives only data for specific page. In that case manualPagination property​ is helpful.

Be mindful to provide an aria-description attribute on the Table to let the assistive technologies announce current pagination state.

import {
flexRender,
getCoreRowModel,
getPaginationRowModel,
useReactTable,
createColumnHelper
} from '@tanstack/react-table';

Rows virtualization#

Rendering large amounts of rows can be inefficient. With virtualization​ (or windowing) of data, only currently visible rows are rendered to DOM to deliver best performance and user experience.

When only a subset of rows are visible, it's a good practice to let all users know which rows are being displayed.

Use the aria-rowcount attribute on the Table to let assistive technologies know the total number of rows available. Table.Row should include aria-rowindex attribute to indicate where each row is in relation to the total available rows.

Virtualization achieves best performance with a fixed row height, so it's not recommended to combine it with overflowStrategy="wrap".

import {
flexRender,
getCoreRowModel,
useReactTable,
createColumnHelper
} from '@tanstack/react-table';
import { useVirtualizer } from '@tanstack/react-virtual';

API Reference#

Table#

Prop
Type
Default
css
StitchesCss
No default value
alignY
enum
"center"
cellSize
enum
"medium"
overflowStrategy
enum
"truncate"
highlightOnHover
boolean
true
syncScroll
boolean
false
aria-label*
string
No default value
rowSeparation
enum
"dividers"
outlined
boolean
true
addHorizontalWhitespace
boolean
false
showCellSeparation
boolean
false

Table.Head#

Prop
Type
Default
css
StitchesCss
No default value
stickyOffset
number
No default value

Table.Header#

Prop
Type
Default
css
StitchesCss
No default value
stickyOffset
number
No default value

Table.ColumnHeader#

Prop
Type
Default
css
StitchesCss
No default value
align
enum
"left"
maxWidth
CSSProperties['maxWidth']
No default value
minWidth
CSSProperties['minWidth']
No default value
width
CSSProperties['width']
No default value
stickyOffset
number
No default value
showDivider
boolean
No default value
onClick
function
No default value
sort
enum
No default value
sortIndicator
React.ReactElement
No default value

Table.ColumnHeaderContent#

Prop
Type
Default
as
enum
div
css
StitchesCss
No default value
overflowStrategy
enum
No default value

Table.SortIndicator#

Prop
Type
Default
css
StitchesCss
No default value

Table.Body#

Prop
Type
Default
css
StitchesCss
No default value

Table.Row#

Prop
Type
Default
css
StitchesCss
No default value
status
enum
No default value
isHighlighted
boolean
false
tone
enum
No default value

Table.Cell#

Prop
Type
Default
css
StitchesCss
No default value
align
enum
"left"
maxWidth
CSSProperties['maxWidth']
No default value
minWidth
CSSProperties['minWidth']
No default value
width
CSSProperties['width']
No default value
stickyOffset
number
No default value
showDivider
boolean
No default value
role
enum
"gridcell"
tone
enum
No default value

Table.CellContent#

In addition to the props below, you can pass Text props.

Prop
Type
Default
as
enum
div
css
StitchesCss
No default value
overflowStrategy
enum
No default value

Table.SelectionCell#

In addition to the props below, you can pass all Table.SelectionCellCheckbox props.

Prop
Type
Default
css
StitchesCss
No default value
align
enum
"left"
width
CSSProperties['width']
No default value
stickyOffset
number
No default value
showDivider
boolean
No default value
role
enum
gridcell
aria-label*
string
No default value

Table.SelectionCellCheckbox#

In addition to the props below, you can pass all Checkbox props.

Prop
Type
Default
css
StitchesCss
No default value
aria-label*
string
No default value

Table.Foot#

Prop
Type
Default
css
StitchesCss
No default value
stickyOffset
number
No default value

Table.Footer#

Prop
Type
Default
css
StitchesCss
No default value
stickyOffset
number
No default value

Table.EmptyState#

Prop
Type
Default
css
StitchesCss
No default value