Skip to main contentSkip to search
Clay
  • Get Started
    • How to Use Clay
    • Composition Philosophy
    • How to Read This Documentation
    • Migrating From v2.x
    • Using Clay in JSPs
  • Components
    • Alert
    • Application Bar
    • Aspect Ratio
    • Autocomplete
    • Badge
    • Breadcrumb
    • Button Group
    • Buttons
    • Card
    • Chart
    • Color Picker
    • Data Provider
    • Date Picker
    • DropDown
    • Empty State
    • Focus Trap
    • Form
      • Checkbox
      • Dual List Box
      • Input
      • Radio Group
      • Select
      • Select Box
      • Toggle Switch
    • Forms Hierarchy
    • Heading
    • Icon
    • Label
    • Layout
    • Link
    • List
    • Loading Indicator
    • Localized Input
    • Management Toolbar
    • Modal
    • Multi Select
    • Multi Step Nav
    • Nav
    • Navigation Bar
    • OverlayMask
    • Pagination
    • Pagination Bar
    • Panel
    • Picker
    • Popover
    • Progress Bar
    • Provider
    • Reduced Motion
    • Sidebar
    • Slider
    • Sticker
    • Table
    • Tabs
    • Text
    • Timelines
    • Time Picker
    • Toolbar
    • Tooltip
    • TreeView
    • Upper Toolbar
    • VerticalBar
    • Vertical Navigation
  • Contributing
  • CSS Framework
    • Paver
    • SCSS
    • Color
    • Grid
    • Content
      • Typography
      • C Kbd
    • Utilities
      • Accessibility
      • Autofit
      • Border
      • C Focus Inset
      • C Inner
      • Color Utilities
      • C Spacing Utilities
      • Display
      • Flex
      • Float
      • Inline Item
      • Overflow
      • Position
      • Shadow
      • Text
      • Vertical Align
      • Visibility
      • Width and Height
    • Playground
  • Examples
  • Docs
  • Sass API
  • Blog
  • Storybook
  • Codesandbox
  • Github
  • Use this menu to toggle between Atlas and Base Themes.

Table

A table is a specific pattern for comparing datasets in a very direct and analytical way.

installyarn add @clayui/core
version3.107.1
  • Examples
  • Markup
  • API

Beta3.107.1View in LexiconCHANGELOGstorybook demos

  • Example
  • Introduction
  • Content
    • Static
    • Dynamic
  • Sorting
    • Asynchronous
  • Nested row
    • Expandable
    • Asynchronous Item

Example

Copied!
Code Sample (expand to see it)

Introduction

Table allows the rendering of static and dynamic content for data-oriented and data-agnostic tables to prevent the developer from having to transform their data to be able to render in a table. Composition is the central point, as is the use of the render props pattern to allow this so that the component can offer OOTB features, such as sorting and nested row.

The component covers the W3C accessibility patterns for the simplest table implementation and also the treegrid pattern for when nested row is used without requiring the developer to have to configure something extremely complex.

WarningTo use the new Table implementation it is necessary to consume the component using the package @clayui/core.

Content

Content rendered in the <Table /> Menu can be done in two different ways, static and dynamic content, the choice depends on the use case.

Static

Static content is when the <Table /> options do not change during the lifecycle of the application or are hardcoded options.

<Table>
	<Head>
		<Cell key="name">Name</Cell>
		<Cell key="type">Type</Cell>
	</Head>

	<Body>
		<Row>
			<Cell>Games</Cell>
			<Cell>File Folder</Cell>
		</Row>
		<Row>
			<Cell>Program Files</Cell>
			<Cell>File Folder</Cell>
		</Row>
	</Body>
</Table>

Dynamic

Unlike static content, dynamic content is when the options can change during the lifecycle of the application or when the data comes from a service. Dynamic content rendering is data agnostic, this allows you to configure how to render the component options regardless of the chosen data structure.

<Table>
	<Head
		items={[
			{
				id: '1',
				name: 'Name',
			},
			{
				id: '2',
				name: 'Type',
			},
		]}
	>
		{(column) => <Cell key={column.id}>{column.name}</Cell>}
	</Head>

	<Body
		defaultItems={[
			{id: 1, name: 'Games', type: 'File folder'},
			{id: 2, name: 'Program Files', type: 'File folder'},
		]}
	>
		{(row) => (
			<Row>
				<Cell>{row.name}</Cell>
				<Cell>{row.type}</Cell>
			</Row>
		)}
	</Body>
</Table>

Sorting

Column sorting is implemented OOTB so the developer doesn't need to worry about implementing the UI details but the developer still needs to add their filter layer since the component is data-agnostic and allows you to do this asynchronously, it is important, especially when your data is paged, that the filter must happen in the backend.

It is also possible to implement your own logic on the client side when your data is predictable, check out the pseudocode.

InfoColumn sorting is only enabled for columns that contain the sortable API defined.
const [sort, setSort] = useState<Sorting | null>(null);
const [items, setItems] = useState([
  {files: 22, id: 1, name: 'Games', type: 'File folder'},
  {files: 7, id: 2, name: 'Program Files', type: 'File folder'},
]);

const filteredItems = useMemo(() => {
  if (!sort) {
    return;
  }

  return items.sort((a, b) => {
    let cmp = new Intl.Collator('en', {numeric: true}).compare(
      a[sort.column],
      b[sort.column]
    );

    if (sort.direction === 'descending') {
      cmp *= -1;
    }

    return cmp;
  });
}, [sort, items])

<Table onSortChange={setSort} sort={sort}>
  <Head>
    <Cell key="name" sortable>
      Name
    </Cell>
    <Cell key="files" sortable>
      Files
    </Cell>
    <Cell key="type" sortable>
      Type
    </Cell>
  </Head>

  <Body defaultItems={filteredItems}>
    ...
  </Body>
</Table>

Asynchronous

Most tables with sorting can have a lot of data and are paged so the sorting must happen on the server side instead of implementing the logic on the client side. You can achieve this level of implementation by composing using the useResource hook.

const {
	resource: items,
	sort,
	sortChange,
} = useResource({
	fetch: (link, init, sort) => {
		const url = new URL(link);

		if (sort) {
			url.searchParams.append('column', String(sort.column));
			url.searchParams.append('direction', sort.direction);
		}

		return fetch(url, init);
	},
	link: 'https://example.com/api/items',
});

<Table onSortChange={setSort} sort={sort}>
	<Head>
		<Cell key="name" sortable>
			Name
		</Cell>
		<Cell key="files" sortable>
			Files
		</Cell>
		<Cell key="type">Type</Cell>
	</Head>

	<Body defaultItems={items}>...</Body>
</Table>;

Nested row

Implementing nested row allows you to render a table as a tree view. It is not necessary that you have to change your composition to render in tree view but just configure the nestedKey property to inform which nested key and the composition can continue in the same way.

When using the nested row pattern, Clay automatically changes the accessibility behavior to use the treegrid recommendation instead of the default behavior.

LimitationThe unique id of a row does not work properly when configured via key in the Row component property to deal with the nodes expandability, it is necessary that the id key is defined in your row data to use as unique id.
Copied!
Code Sample (expand to see it)

Expandable

Expanding nodes is done OOTB in the component but it is also possible to control the state to modify behaviors if necessary or use it to save a session to improve the user experience.

const [expandedKeys, setExpandedKeys] = useState(new Set());

<Table expandedKeys={expandedKeys} onExpandedChange={setExpandedKeys}>
	<Head items={columns}>
		{(column) => <Cell key={column.id}>{column.name}</Cell>}
	</Head>

	<Body defaultItems={rows}>
		{(row) => (
			<Row items={columns}>
				{(column) => <Cell>{row[column.id]}</Cell>}
			</Row>
		)}
	</Body>
</Table>;

Asynchronous Item

When the tree is very large with a lot of data on a single node, loading the data asynchronously is essential to reduce the initial data payload and memory space. Table supports asynchronous node loading when the user expands a node.

  • When returning void, null or undefined the Table will do nothing.
  • When returning the items will add to the tree.
WarningIf you have an error in the asynchronous call of the onLoadMore method, only the suppression is done and an error is thrown on the console.

When adding a new asynchronous item to the tree, the onItemsChange method is respectively called to update the tree with a new value if the items prop is controlled.

<Table
	onLoadMore={async (item) => {
		return await fetch(`example.com/tree/item?parent_id=${item.id}`);
	}}
>
	<Head items={columns}>
		{(column) => <Cell key={column.id}>{column.name}</Cell>}
	</Head>

	<Body
		defaultItems={[
			{id: 1, name: 'Games', type: 'File folder'},
			{id: 2, name: 'Program Files', type: 'File folder'},
		]}
	>
		{(row) => (
			<Row>
				<Cell>{row.name}</Cell>
				<Cell>{row.type}</Cell>
			</Row>
		)}
	</Body>
</Table>

How can this be improved? Create an issue!