Dynamic table

A dynamic table displays rows of data with built-in pagination, sorting, and re-ordering functionality.
All examples use the following data model in their tables for easier readability.
  interface President {
    id: number;
    name: string;
    party: string;
    term: string;
  }

  // applied as rows in the form
  const rows = presidents.map((president: President, index: number) => ({
    key: `row-${index}-${president.name}`,
    cells: [
      {
        key: createKey(president.name),
        content: (
          <NameWrapper>
            <AvatarWrapper>
              <Avatar name={president.name} size="medium" />
            </AvatarWrapper>
            <Link href="https://atlassian.design">{president.name}</Link>
          </NameWrapper>
        ),
      },
      {
        key: createKey(president.party),
        content: president.party,
      },
      {
        key: president.id,
        content: president.term,
      },
    ]
  })

Uncontrolled

Dynamic table manages sorting, pagination, loading, and drag and drop state management by default. If this functionality isn't needed, use the native HTML table element.

CommentActions
George Washington
None, Federalist1789-17970
John Adams
Federalist1797-18011
Thomas Jefferson
Democratic-Republican1801-18092
James Madison
Democratic-Republican1809-18173
James Monroe
Democratic-Republican1817-18254
import React from 'react'; import DynamicTable from '@atlaskit/dynamic-table'; import { head, rows } from './content/sample-data'; export default function TableUncontrolled() { return ( <DynamicTable head={head} rows={rows} rowsPerPage={5} defaultPage={1} loadingSpinnerSize="large" isRankable testId="table" /> ); }

Controlled

In a controlled dynamic table, you need to manage sorting, drag and drop, and pagination on your own. If you require this functionality, use the stateless dynamic table component.

CommentActions
Dwight David Eisenhower
Republican1953-196123
Harry S Truman
Democrat1945-195322
Franklin Delano Roosevelt
Democrat1933-194521
Herbert C. Hoover
Republican1929-193320
Calvin Coolidge
Republican1923-192919
import React, { useState } from 'react'; import Banner from '@atlaskit/banner'; import ButtonGroup from '@atlaskit/button/button-group'; import Button from '@atlaskit/button/new'; import { DynamicTableStateless } from '@atlaskit/dynamic-table'; import WarningIcon from '@atlaskit/icon/glyph/warning'; import { head, rows } from './content/sample-data'; export default function TableControlled() { const [pageNumber, setPageNumber] = useState(3); const navigateTo = (pageNumber: number) => { setPageNumber(pageNumber); }; return ( <> <Banner appearance="warning" icon={<WarningIcon label="" secondaryColor="inherit" />}> This is a stateless table example, which doesn't have pagination support. To navigate pages, use the "Previous page" and "Next page" buttons. </Banner> <ButtonGroup label="Paging navigation"> <Button isDisabled={pageNumber === 1} onClick={() => navigateTo(pageNumber - 1)}> Previous Page </Button> <Button isDisabled={pageNumber === 5} onClick={() => navigateTo(pageNumber + 1)}> Next Page </Button> </ButtonGroup> <DynamicTableStateless head={head} rows={rows} rowsPerPage={5} page={pageNumber} loadingSpinnerSize="large" isLoading={false} isFixedSize sortKey="term" sortOrder="DESC" onSort={() => console.log('onSort')} onSetPage={() => console.log('onSetPage')} /> </> ); }

Sorting

Sorting a dynamic table takes place using the key set on each cell. Note that the type of key will affect the sorted result. For example, numeric and string keys will result in different orderings. Avoid using objects or React nodes as keys.

Example sorting with DynamicTable
1d
2c
3a
4b
import React, { type FC } from 'react'; import DynamicTable from '@atlaskit/dynamic-table'; const caption = 'Example sorting with DynamicTable'; const head = { cells: [ { key: 'number', content: 'Number', isSortable: true, }, { key: 'string', content: 'String', isSortable: true, }, ], }; const rows = [ [1, 'd'], [2, 'c'], [3, 'a'], [4, 'b'], ].map(([number, letter]) => ({ key: number.toString(), cells: [ { key: number, content: number, }, { key: letter, content: letter, }, ], })); const SortingExample: FC = () => ( // eslint-disable-next-line @atlaskit/ui-styling-standard/enforce-style-prop -- Ignored via go/DSP-18766 <div style={{ maxWidth: 800 }}> <DynamicTable caption={caption} head={head} rows={rows} isFixedSize defaultSortKey="number" defaultSortOrder="ASC" /> </div> ); export default SortingExample;

Loading states

Dynamic table uses a spinner to denote loading state. This is toggled by the isLoading prop.

Table content is set to 20% opacity in this loading state, using the opacity.loading token.

CommentActions
George Washington
None, Federalist1789-17970
John Adams
Federalist1797-18011
Thomas Jefferson
Democratic-Republican1801-18092
James Madison
Democratic-Republican1809-18173
James Monroe
Democratic-Republican1817-18254
import React, { useState } from 'react'; import Button from '@atlaskit/button/new'; import { DynamicTableStateless } from '@atlaskit/dynamic-table'; import { head, rows } from './content/sample-data'; const LoadingPartialPageExample = () => { const [isLoading, setIsLoading] = useState(false); return ( <div> <Button onClick={() => setIsLoading((loading) => !loading)}>Toggle loading</Button> <DynamicTableStateless head={head} rows={rows} rowsPerPage={5} page={1} isLoading={isLoading} /> </div> ); }; export default LoadingPartialPageExample;

Empty state

Use the emptyView prop to show an empty state in the dynamic table. Empty states communicate that the table has no content to show. If there's an action that people must take to create or show table content, add this to the empty state so they know how to proceed. See empty state guidelines for more guidance.

CommentActions

The table is empty and this is the empty view

import React from 'react'; import { DynamicTableStateless } from '@atlaskit/dynamic-table'; import { head } from './content/sample-data'; const EmptyViewExample = () => ( <DynamicTableStateless head={head} emptyView={<h2>The table is empty and this is the empty view</h2>} /> ); export default EmptyViewExample;

You can enable or disable pagination with the rowsPerPage prop. If the rowsPerPage prop is set and the number of rows exceed one page, the pagination component will show below the table.

List of US Presidents
CommentActions
George Washington
None, Federalist1789-17970
John Adams
Federalist1797-18011
Thomas Jefferson
Democratic-Republican1801-18092
James Madison
Democratic-Republican1809-18173
James Monroe
Democratic-Republican1817-18254
import React from 'react'; import DynamicTable from '@atlaskit/dynamic-table'; import { head, rows } from './content/sample-data'; export default () => { return ( <DynamicTable caption="List of US Presidents" head={head} rows={rows} rowsPerPage={5} defaultPage={1} isFixedSize defaultSortKey="term" defaultSortOrder="ASC" onSort={() => console.log('onSort')} onSetPage={() => console.log('onSetPage')} /> ); };

Drag and drop

Drag and drop functionality is built into the dynamic table. You can enable it using the isRankable prop. This lets people drag rows and rank them in different orders.

Fixed size
Loading
List of US Presidents
CommentActions
George Washington
None, Federalist1789-17970
John Adams
Federalist1797-18011
Thomas Jefferson
Democratic-Republican1801-18092
James Madison
Democratic-Republican1809-18173
James Monroe
Democratic-Republican1817-18254
import React, { useCallback, useState } from 'react'; import DynamicTable from '@atlaskit/dynamic-table'; import Toggle from '@atlaskit/toggle'; import { createHead, rows } from './content/sample-data'; export default function DynamicTableRankableExample() { const [isFixedSize, setIsFixedSize] = useState(false); const [isLoading, setIsLoading] = useState(false); const onToggleFixedChange = useCallback(() => { setIsFixedSize((isFixedSize) => !isFixedSize); }, [setIsFixedSize]); const onLoadingChange = useCallback(() => { setIsLoading((isLoading) => !isLoading); }, [setIsLoading]); return ( <div> <div> <Toggle label="Fixed size" onChange={onToggleFixedChange} isChecked={isFixedSize} /> Fixed size </div> <div> <Toggle label="Loading" onChange={onLoadingChange} isChecked={isLoading} /> Loading </div> <DynamicTable caption="List of US Presidents" head={createHead(isFixedSize)} rows={rows} rowsPerPage={5} defaultPage={1} isRankable isLoading={isLoading} onRankStart={(params) => console.log('onRankStart', params)} onRankEnd={(params) => console.log('onRankEnd', params)} onSort={() => console.log('onSort')} onSetPage={() => console.log('onSetPage')} testId="my-table" /> </div> ); }

Overflow

Larger tables or tables that cannot be constrained easily can use horizontal scroll. This isn't supported directly by dynamic table, but the component can be easily extended to support this.

Be mindful that horizontally scrolling tables can cause accessibility issues if there isn't enough visual affordance to indicate that the table has a scroll. For this reason, we recommend finding ways to simplify the table before opting for a horizontal scroll solution.

CommentActions
George Washington
None, Federalist1789-17970
John Adams
Federalist1797-18011
Thomas Jefferson
Democratic-Republican1801-18092
James Madison
Democratic-Republican1809-18173
James Monroe
Democratic-Republican1817-18254
John Quincy Adams
Democratic-Republican1825-18295
Andrew Jackson
Democrat1829-18376
Martin van Buren
Democrat1837-18417
William H. Harrison
Whig18418
John Tyler
Whig1841-18459
/** * @jsxRuntime classic * @jsx jsx */ import { css, jsx } from '@compiled/react'; import DynamicTable from '@atlaskit/dynamic-table'; import { head, rows } from './content/sample-data'; const wrapperStyles = css({ overflowX: 'auto', }); const overflowStyles = css({ width: 1000, }); const HeadlessExample = () => ( <div css={wrapperStyles}> <div css={overflowStyles}> <DynamicTable head={head} rows={rows.slice(0, 10)} /> </div> </div> ); export default HeadlessExample;

Custom column span

Individual cells can use colSpan to make cells span across more than one column.

Class timetable
TimeMondayTuesdayWednesdayThursdayFriday
9:00MathHistoryScienceComputingMath
12:00LUNCH
13:00ScienceHistoryPsychologyComputingBusiness
import React from 'react'; import DynamicTable from '@atlaskit/dynamic-table'; const days = ['Time', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday']; const head = { cells: days.map((day) => ({ key: day, content: day, })), }; const rows = [ { key: `morning-row`, cells: ['9:00', 'Math', 'History', 'Science', 'Computing', 'Math'].map((content, index) => ({ key: index, content, })), }, { key: 'midday-row', cells: [ { key: 0, content: '12:00', }, { key: 1, content: 'LUNCH', colSpan: 5, }, ], }, { key: 'afternoon-row', cells: ['13:00', 'Science', 'History', 'Psychology', 'Computing', 'Business'].map( (content, index) => ({ key: index, content, }), ), }, ]; const CustomColSpanExample = () => ( <DynamicTable caption="Class timetable" head={head} rows={rows} loadingSpinnerSize="large" isLoading={false} isFixedSize /> ); export default CustomColSpanExample;

Highlighted row

You can highlight rows with highlightedRowIndex. Highlights provide additional visual prominence to a row. For example, you could use highlighted rows to show new rows that are added to a table.

Keep in mind that people with visual disabilities may not notice when rows are highlighted, so don’t rely on highlights alone to convey information. Never use highlighted rows to indicate that a person has selected or focused on the row.

CommentActions
George Washington
None, Federalist1789-17970
John Adams
Federalist1797-18011
Thomas Jefferson
Democratic-Republican1801-18092
James Madison
Democratic-Republican1809-18173
James Monroe
Democratic-Republican1817-18254
John Quincy Adams
Democratic-Republican1825-18295
Andrew Jackson
Democrat1829-18376
Martin van Buren
Democrat1837-18417
William H. Harrison
Whig18418
John Tyler
Whig1841-18459
import React from 'react'; import DynamicTable from '@atlaskit/dynamic-table'; import { head, rows } from './content/sample-data'; const rowsWithHighlightedRow = [...rows]; rowsWithHighlightedRow[6].isHighlighted = true; export default () => ( <DynamicTable head={head} rows={rowsWithHighlightedRow} rowsPerPage={10} page={1} testId="the-table" /> );

Interactive row

Rows can be interactive if you provide an onClick or onKeydown handler to the row.

Click in a row to highlight it

CommentActions
George Washington
None, Federalist1789-17970
John Adams
Federalist1797-18011
Thomas Jefferson
Democratic-Republican1801-18092
James Madison
Democratic-Republican1809-18173
James Monroe
Democratic-Republican1817-18254
John Quincy Adams
Democratic-Republican1825-18295
Andrew Jackson
Democrat1829-18376
Martin van Buren
Democrat1837-18417
William H. Harrison
Whig18418
John Tyler
Whig1841-18459
import React from 'react'; import { DynamicTableStateless } from '@atlaskit/dynamic-table'; import type { RowType } from '@atlaskit/dynamic-table/types'; import { rows as allRows, head } from './content/sample-data'; const rows = allRows.slice(0, 10); const extendRows = ( rows: Array<RowType>, onClick: (e: React.MouseEvent, rowIndex: number) => void, ) => { return rows.map((row, index) => ({ ...row, onClick: (e: React.MouseEvent) => onClick(e, index), })); }; interface StatelessState { highlightedRowIndex?: number[]; } class RegularStatelessExample extends React.Component<{}, StatelessState> { state = { highlightedRowIndex: [], }; onRowClick = (e: React.MouseEvent, rowIndex: number) => { this.setState(({ highlightedRowIndex }) => { const newHighlightedRowIndex = [...(highlightedRowIndex || [])]; const existingIndex = newHighlightedRowIndex.indexOf(rowIndex); if (existingIndex > -1) { newHighlightedRowIndex.splice(existingIndex, 1); } else { newHighlightedRowIndex.push(rowIndex); } return { highlightedRowIndex: newHighlightedRowIndex }; }); }; render() { return ( <DynamicTableStateless head={head} highlightedRowIndex={this.state.highlightedRowIndex} rows={extendRows(rows, this.onRowClick)} /> ); } } export default () => { return ( <> <h4>Click in a row to highlight it</h4> <RegularStatelessExample /> </> ); };
Was this page helpful?
We use this feedback to improve our documentation.