Components
Table
Table is used to display structured information in a neat and organized manner. Tables can make it easier for users to scan and compare information.
React
const Preview = () => { return ( <Table> <caption>Table caption</caption> <Table.Head> <Table.Row> <Table.HeaderCell>Header 1</Table.HeaderCell> <Table.HeaderCell>Header 2</Table.HeaderCell> <Table.HeaderCell>Header 3</Table.HeaderCell> </Table.Row> </Table.Head> <Table.Body> <Table.Row> <Table.Cell>Cell 1</Table.Cell> <Table.Cell>Cell 2</Table.Cell> <Table.Cell>Cell 3</Table.Cell> </Table.Row> <Table.Row> <Table.Cell>Cell 4</Table.Cell> <Table.Cell>Cell 5</Table.Cell> <Table.Cell>Cell 6</Table.Cell> </Table.Row> </Table.Body> <Table.Foot> <Table.Row> <Table.HeaderCell>Footer 1</Table.HeaderCell> <Table.HeaderCell>Footer 2</Table.HeaderCell> <Table.HeaderCell>Footer 3</Table.HeaderCell> </Table.Row> </Table.Foot> </Table> ); }; render(<Preview />)
Use the Table when
- the goal is to help users compare information easily
- the data needs a clear structure with rows and columns
- the content can benefit from sorting
Avoid using the Table when
- the purpose is to create layout or visually organise content on a page
- the information is better suited to a list or card view
- the content is very limited, for example only one column or very few data points without a need for comparison
- the table becomes difficult to read on mobile because the data set is large
Example
Zebra stripes
React
const ZebraEn = () => { return ( <Table zebra> <caption>Number of applications per month</caption> <Table.Head> <Table.Row> <Table.HeaderCell>Month</Table.HeaderCell> <Table.HeaderCell>2023</Table.HeaderCell> <Table.HeaderCell>2024</Table.HeaderCell> </Table.Row> </Table.Head> <Table.Body> <Table.Row> <Table.HeaderCell scope='row'>January</Table.HeaderCell> <Table.Cell>1 230</Table.Cell> <Table.Cell>1 450</Table.Cell> </Table.Row> <Table.Row> <Table.HeaderCell scope='row'>February</Table.HeaderCell> <Table.Cell>980</Table.Cell> <Table.Cell>1 120</Table.Cell> </Table.Row> <Table.Row> <Table.HeaderCell scope='row'>March</Table.HeaderCell> <Table.Cell>1 150</Table.Cell> <Table.Cell>1 300</Table.Cell> </Table.Row> </Table.Body> </Table> ); }; render(<ZebraEn />)
With border
Use border to add a visible outline around the table.
This can help users distinguish it from other content on the page and provides a more structured presentation of the data.
React
const Border = () => { return ( <Table border> <Table.Head> <Table.Row> <Table.HeaderCell>Header 1</Table.HeaderCell> <Table.HeaderCell>Header 2</Table.HeaderCell> </Table.Row> </Table.Head> <Table.Body> <Table.Row> <Table.Cell>Cell 1</Table.Cell> <Table.Cell>Cell 2</Table.Cell> </Table.Row> </Table.Body> </Table> ); }; render(<Border />)
Sorting
You can show that the table supports sorting based on the content of a column.
- You can sort in 3 directions:
"none"no sorting"ascending"increasing order"descending"decreasing order
This gives users a visual indication of the sorting status and makes it possible to implement custom sorting logic.
React
const SortableEn = () => { const dummyData = [ { id: 1, name: 'Lise Example', email: 'lise@example.com', phone: '22345678', }, { id: 2, name: 'Kari Example', email: 'kari@example.com', phone: '87654321', }, { id: 3, name: 'Ola Example', email: 'ola@example.com', phone: '32345678', }, { id: 4, name: 'Per Example', email: 'per@example.com', phone: '12345678', }, ]; const [sortField, setSortField] = useState< keyof (typeof dummyData)[0] | null >(null); const [sortDirection, setSortDirection] = useState<TableHeaderCellProps['sort']>(undefined); const handleSort = (field: keyof (typeof dummyData)[0]) => { if (sortField === field && sortDirection === 'descending') { setSortField(null); setSortDirection(undefined); } else { setSortField(field); setSortDirection( sortField === field && sortDirection === 'ascending' ? 'descending' : 'ascending', ); } }; const sortedData = [...dummyData].sort((a, b) => { if (sortField === null) return 0; if (a[sortField] < b[sortField]) return sortDirection === 'ascending' ? -1 : 1; if (a[sortField] > b[sortField]) return sortDirection === 'ascending' ? 1 : -1; return 0; }); return ( <Table> <Table.Head> <Table.Row> <Table.HeaderCell sort={sortField === 'name' ? sortDirection : 'none'} onClick={() => handleSort('name')} > Name </Table.HeaderCell> <Table.HeaderCell>Email</Table.HeaderCell> <Table.HeaderCell sort={sortField === 'phone' ? sortDirection : 'none'} onClick={() => handleSort('phone')} > Phone </Table.HeaderCell> </Table.Row> </Table.Head> <Table.Body> {sortedData.map((row) => ( <Table.Row key={row.id}> <Table.Cell>{row.name}</Table.Cell> <Table.Cell>{row.email}</Table.Cell> <Table.Cell>{row.phone}</Table.Cell> </Table.Row> ))} </Table.Body> </Table> ); }; render(<SortableEn />)
Fixed column width
Fixed column widths are especially useful in tables with pagination or other dynamic content updates, where stable widths improve the user experience.
React
const FixedTable = () => { const rows = Array.from({ length: 3 }, (_, i) => i + 1); return ( <Table style={{ tableLayout: 'fixed', }} > <Table.Head> <Table.Row> <Table.HeaderCell>Header 1</Table.HeaderCell> <Table.HeaderCell>Header 2</Table.HeaderCell> <Table.HeaderCell>Header 3</Table.HeaderCell> </Table.Row> </Table.Head> <Table.Body> {rows.map((row) => ( <Table.Row key={row}> <Table.Cell>{`Cell ${row}1`}</Table.Cell> <Table.Cell>{`Cell ${row}2`}</Table.Cell> <Table.Cell>{`Cell ${row}3`}</Table.Cell> </Table.Row> ))} </Table.Body> </Table> ); }; render(<FixedTable />)
Numbers in a table
When numbers appear in a table and are meant to be compared, align them to the right within the cell.
React
const NumbersEn = () => ( <Table style={{ tableLayout: 'fixed', fontVariantNumeric: 'tabular-nums', }} > <caption>Number of applications per month</caption> <Table.Head> <Table.Row> <Table.HeaderCell scope='col'>Month</Table.HeaderCell> <Table.HeaderCell scope='col' style={{ textAlign: 'right' }}> 2023 </Table.HeaderCell> <Table.HeaderCell scope='col' style={{ textAlign: 'right' }}> 2024 </Table.HeaderCell> </Table.Row> </Table.Head> <Table.Body> <Table.Row> <Table.HeaderCell scope='row'>January</Table.HeaderCell> <Table.Cell style={{ textAlign: 'right' }}>1 230</Table.Cell> <Table.Cell style={{ textAlign: 'right' }}>1 450</Table.Cell> </Table.Row> <Table.Row> <Table.HeaderCell scope='row'>February</Table.HeaderCell> <Table.Cell style={{ textAlign: 'right' }}>980</Table.Cell> <Table.Cell style={{ textAlign: 'right' }}>1 120</Table.Cell> </Table.Row> <Table.Row> <Table.HeaderCell scope='row'>March</Table.HeaderCell> <Table.Cell style={{ textAlign: 'right' }}>1 150</Table.Cell> <Table.Cell style={{ textAlign: 'right' }}>1 300</Table.Cell> </Table.Row> </Table.Body> </Table> ); render(<NumbersEn />)
Guidelines
Use the Table to structure and present data clearly in rows and columns.
- Content in tables should be left-aligned, except for numbers, which should be right-aligned to make comparison easier.
- Use
<Table.HeaderCell>for header cells, not<Table.Cell>. A cell counts as a header when it describes the content of the same row or column. - In table rows with several actions, you can use a menu to save space if the actions do not need to be visible at all times.
Text
Avoid long texts in table cells. Keep content short and concise so it is easy to scan across rows and columns. If you need to show more information, consider linking to a detail page. Use headers to tell users what type of content they will find in the rows and columns.
As a general rule, content in cells should be left-aligned to make it easier to read and scan. The exception is numbers and currency and sometimes form elements and buttons.
Edit this page on github.com (opens in a new tab)