Skip to content

Tables

The following table has a default style. But in future, there will be several extensions and styles to choose from.

Accessibility

Tables do both serve as a way of navigation for screen readers and other assertive technologies. But they also help to give data an ordered structure.

Use the documentation from MDN – The Table element for more information on making semantic correct tables, including scope, align, colSpan and rowSpan.

Here is a list of things you may follow along in order to ensure your coded tables still are accessible:

  • Keep a semantic correct structure.
  • Let tables align the column width, when possible.
  • Do not use CSS display property on any table element.
  • Do not overwrite styles in general, but rather get in touch with DNB UX.
  • Never put a table inside a table.
  • Text inside tables do not need to be wrapped inside a paragraph as well. They give screen readers no additional useful information.

Table header components

  • <Th.SortButton /> to be used for additional sorting functionality.
  • <Th.HelpButton /> to be used for help related content.

Alignment

Use e.g. align="right" on a <Th>, <Td> or <Tr> to align a table header or a table data element.

Fixed layout

You may consider using table-layout: fixed;. You can use the modifier property fixed for doing so and combine it with CSS e.g. width: 40% on specific table headers.

Scrollable

Depending on your situation, you may want to wrap your Table within Table.ScrollView:

import { Table } from '@dnb/eufemia'
render(
<Table.ScrollView>
<Table />
</Table.ScrollView>
)

Sticky header

You have two options (both have their downsides):

  1. use sticky={true}. It works even when using a Table.ScrollView or a overflow: hidden; is used on any parent elements. And it works inside a Drawer as well. The downside is, that it uses JavaScript and the browser may drop some frames, which results in a potential flickering during scrolling.

  2. use sticky="css-position" for using the CSS position: sticky; method. It is super smooth. But then you can not use a overflow: hidden; or overflow: auto; on any parent elements. This is a know issue happening on every modern browser.

Method no. 2 should be used when a max-height is set to the wrapping Table.ScrollView e.g.:

<Table.ScrollView style={{ maxHeight: '20rem' }}>
<Table sticky="css-position" />
</Table.ScrollView>

Have a look at this example.

Sortable table

Optionally, make use of the following React Hook to handle the Th.SortButton directions.

It can be used as a "controller" for your own sorting logic of your data.

By default, it will cycle trough three stages ['asc', 'desc', 'off'].

Show how to use the useHandleSortState React Hook.

import useHandleSortState from '@dnb/eufemia/components/table/useHandleSortState'
// You can also provide a default that will be used as the fallback e.g.
const defaultOptions = { direction: 'asc', modes: ['asc', 'desc', 'off'] }
export const YourComponent = () => {
const { sortState, sortHandler, activeSortName } = useHandleSortState(
{
// Define your column names with options (optional)
column1: { active: true }, //
column2: { direction: 'desc', modes: ['asc', 'desc'] }, // overwrite the defaultOptions
column3: { modes: ['asc', 'off'] }, // will only allow one direction
column4: {}, // etc.
},
defaultOptions
)
// Use these properties for your custom sorting logic
console.log(sortState.column1.direction) // returns either "asc", "desc" or "off"
console.log(activeSortName) // returns the current active one: "column1" (returns null when nothing is active)
// Handle your logic
useEffect(() => {
switch (sortState.column1.direction) {
default:
case 'asc':
setYourLocalState(mockData.sort(compareFunctionAsc))
break
case 'desc':
setYourLocalState(mockData.sort(compareFunctionsDesc))
break
case 'off':
setYourLocalState(mockData)
break
}
}, [sortState.column1.direction])
return (
<Table>
<thead>
<Tr>
<Th
sortable
active={sortState.column1.active}
reversed={sortState.column1.reversed}
>
<Th.SortButton
text="Column 1"
title="Sort this column"
on_click={sortHandler.column1}
/>
</Th>
</Tr>
</thead>
</Table>
)
}

Demos

Basic table

NB: In this example, the sort buttons do react on your input. But will not change the table data.

A Table Caption
Column
Help Button
Row 1Row 1Row 1Row 1
Row 2Row 2Row 2Row 2

Row 3 with paragraph

Row 3 with code

Row 3 with medium paragraph

Row 3 with medium text

Complex table

You can force a row to overwrite the automated odd/even counting by providing e.g. variant="even" to a <Tr />. You can use this in combination with rowSpan.

NB: The table header in the first column needs to have scope="row"!

A Table Caption
Column 2
newline
Column 3 that spans
Row 1+2 HeaderRow 1 that spansRow 1Row 1
Row 2Row 2
Row 3 Header
newline
Row 3noSpacing + align="right"
Row 4 HeaderRow 4Row 4

Row scope headers only

This table has only scope="row" and scope="rowgroup" headers – without the default scope="col".

A Table Caption
Header ARow 1Row 1
Header BRow 2Row 2

Fixed table

A Table Caption
Column 1Column 2Column 3Column 4Column 5Column 6Column 7Column 8
Row 1Row 1Row 1Row 1Row 1Row 1Row 1Row 1
Row 2Row 2Row 2Row 2Row 2Row 2Row 2Row 2
Row 3Row 3Row 3Row 3Row 3Row 3Row 3Row 3
Row 4Row 4Row 4Row 4Row 4Row 4Row 4Row 4

Medium and small sized

A Table Caption
ColumnColumn
Row 1Row 1Row 1

Row 2 with paragraph

Row 2 with medium paragraph

Row 2 with medium text

A small sized table is only for special circumstances, where a lot of data needs to be shown on the screen at the same time.

A Table Caption
ColumnColumn
Row 1Row 1Row 1

Row 2 with paragraph

Row 2 with medium paragraph

Row 2 with medium text

Table with accordion rows

The second example uses both a border and an outline.

A Table Caption
Column AColumn BColumn CColumn D
Row 1Row 1
Row 2Row 2
Row 3Row 3
A Table Caption
Column AColumn BColumn CColumn D
Row 1Row 1Row 1
Row 2Row 2Row 2
Row 3Row 3Row 3

Table with sticky header

A Table Caption
Header

Row 1 with p

Row 1 with codeRow 1 with spanRow 1
Column which spans over two columnsRow 2Row 2
Row 3Row 3Row 3Row 3
FooterSum

Table with a max height

A sticky table header with sticky="css-position" and max-height on the Table.ScrollView.

Column 1Column 2Column 3Column 4
Row 1Row 1Row 1Row 1
Row 2Row 2Row 2Row 2
Row 3Row 3Row 3Row 3
Row 4Row 4Row 4Row 4
Row 5Row 5Row 5Row 5
Row 6Row 6Row 6Row 6
Row 7Row 7Row 7Row 7
Row 8Row 8Row 8Row 8
Row 9Row 9Row 9Row 9
Row 10Row 10Row 10Row 10
Row 11Row 11Row 11Row 11
Row 12Row 12Row 12Row 12

Several tables in one container

Show how the import and syntax is structured.

<TableContainer>
<TableContainer.Head>
<H2>Heading</H2>
</TableContainer.Head>
<TableContainer.Body>
<Table>{'hei'}</Table>
<Table>{'hei'}</Table>
</TableContainer.Body>
<TableContainer.Foot>
<P>Footer</P>
</TableContainer.Foot>
</TableContainer>

Header

Text

Table One
I have a superscript 1Column 2Column 3Column 4
Row 1Row 1Row 1Row 1
Row 2Row 2Row 2Row 2
Table Two
Column 1Column 2Column 3Column 4
Row 1Row 1Row 1Row 1
Row 2Row 2Row 2
Row 3Row 3Row 3
Row Header GroupRow 1Row 1
Row 2Row 2

Footer

With no (empty) head and foot content.

Column 1Column 2Column 3Column 4
Row 1Row 1Row 1Row 1
Row 2Row 2Row 2Row 2

Table with long header text (wrapping)

A Table Caption
Static long header senectus ornare convallis ut at erat imperdiet commodo

col span of 4

Table with pagination

Side 1

Example usage without and with classes

Header
Row 1Row 1Row 1
Row 2Row 2Row 2
Row 3Row 3Row 3
.dnb-table__th
.dnb-table__tr--even > .dnb-table__td
.dnb-table__tr--odd > .dnb-table__td