#StandWithUkraine

Table React-only

Open in Storybook Open in Figma

What follows is a simple table. While we have some more real-world-like examples under the dedicated Table Examples page, make sure to visit the Storybook linked above too.

Loading demo…

Layout

Table comes with a layout prop, and there are three different layouts to choose from.

  • DEFAULT: A simple table that is able to make good use of the available horizontal space.
  • COMPLEX: A more versatile Table that also requires more control. This layout supports advanced cell styles beyond the typical row/column look, e.g. a something like a details cell, which will be explained under Types.
  • FIXED: While the two previous layouts turn the table into something that looks more like a labelled list on tiers XS and SM, this responsive behaviour is turned off for a fixed Table, i.e. columns are displayed on every tier. In addition, it will show a scrollbar on any tier that does not have enough room to display all columns/rows. Otherwise same as the DEFAULT layout.

HINT The key to achieve a certain design is to specify column widths.

Nesting tables

Having a table inside the table cell of another table makes it difficult to navigate it for customers using a screen reader and should be avoided. That said, doing so is still needed for legacy reasons. Whenever you have to nest tables, keep the following in mind:

  • You cannot put a non-complex table inside a complex table, and vice-versa.
  • As both CollapsibleTable and Transaction are complex tables under the hood, a nested table in the expanding details must also be a complex table.

Headers

There are four types of table headings: 1. the table caption, 2. column headers, 3. row headers, and also 4. section headers. For the first type, an optional caption may be set to let people know what the table content is about. In George, the caption is never shown visually, only people using assistive technology will be able to get the content. The other headers are explained below.

If a table is empty or a search in the table delivers no results, the entire table, including the table header, is replaced by a status message. However, other components like search or filters remain unaffected.

Column & row headers

On tiers MD and above, the header is shown on top, while on smaller screens our responsive tables turn into a long vertical scroll, where each heading is repeated as label before the individual column content.

Rules For Developers
  • Showing the heading as leading label on smaller tiers can be turned off per column by setting labelize to false.
  • The top header on larger tiers can be hidden visually by setting showHeaders to NEVER.
  • Column headers can stick to the top of the viewport when the user scrolls down. To achieve this, add sticky prop on the Table component. When there’s a need to display other elements above it, set stickyOffset.
  • Column headers are mandatory in George, there are no tables with row headers only.
  • Row headers may be used in addition to column headers. They are created by adding a TableCell with its header prop set to true to a TableRow that lives in the TableBody area. Usually such cells come first in their row.

Section headers BETA

Open in Storybook

Whenever a number of table rows form a group, section headers may be added by using the TableSection component.

TableSection comes with a variant prop, with HEADING being the default. This is exactly what’s needed to create a section header. The second variant is ADVERTISEMENT, so not a heading at all. Instead, a table row with random nonsense – unrelated to the table – will be injected at an arbitrary position.

Loading demo…
Rules For Developers
  • TableSection is primarily used to provide visual cues in longer tables, e.g. by grouping all rows that belong to a certain month with a section header that displays the month in a “MMMM yyyy” format.
  • You can use multiple TableBody elements in your table and start each one with a section header, but this is not mandatory.
  • Contrary to what is shown under “Complex headers” below, when using TableSection, neither the scope nor the headers attribute is in use. Hence the association between table cells an their section header is purely visual. This is because the information is usually redundant, i.e. anyhow part of the data cells.

Complex headers

The following code is not necessarily an example that will ever be created by GDS Components but it showcases the “Complex header structures” described in the rules below.

html<table>
  <caption>
    Accessible Table
  </caption>
  <thead>
    <tr>
      <td></td>
      <th id="col-heading-1" scope="col">col-heading .1</th>
      <th id="col-heading-2" scope="col">col-heading .2</th>
      <th id="col-heading-3" scope="col">col-heading .3</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <th id="section-heading-A" colspan="3" scope="colgroup">section heading A</th>
    </tr>
    <tr>
      <th headers="section-heading-A" id="row-heading-1" scope="row">row heading 1</th>
      <td headers="section-heading-A row-heading-1 col-heading-1">value 1.1</td>
      <td headers="section-heading-A row-heading-1 col-heading-2">value 1.2</td>
    </tr>
    <tr>
      <th headers="section-heading-A" id="row-heading-2" scope="row">row heading 2</th>
      <td headers="section-heading-A row-heading-2 col-heading-1">value 2.1</td>
      <td headers="section-heading-A row-heading-2 col-heading-2">value 2.2</td>
    </tr>
    <tr>
      <th id="section-heading-B" colspan="3" scope="colgroup">section heading B</th>
    </tr>
    <tr>
      <th headers="section-heading-B" id="row-heading-3" scope="row">row heading 3</th>
      <td headers="section-heading-B row-heading-3 col-heading-1">value 3.1</td>
      <td headers="section-heading-B row-heading-3 col-heading-2">value 3.2</td>
    </tr>
    <tr>
      <th headers="section-heading-B" id="row-heading-4" scope="row">row heading 4</th>
      <td headers="section-heading-B row-heading-4 col-heading-1">value 4.1</td>
      <td headers="section-heading-B row-heading-4 col-heading-2">value 4.2</td>
    </tr>
  </tbody>
</table>
Rules For Developers
  • Simple tables with one header: For tables that feature only a header on top, simply mark up header cells with <th> and data cells with <td> elements.
  • Tables with a row header AND a column header: For tables with more than one header, define the direction of each header by setting the scope attribute to col or row.
  • Header cells that span multiple columns and/or rows: Define column and row groups and set the range of the header cells using the colgroup and rowgroup values of the scope attribute.
  • Complex header structures: For tables that are so complex that header cells can’t be associated in a strictly horizontal or vertical way, IDs and headers attributes may be used to connect headers to data cells explicitly.

Columns & cells

The following properties can be assigned to columns or individual cells, but assignment on column level is recommended. Please note that an assignment on column level supercedes the cell level, so to achieve the cell assignment, no column assignment must be set.

Types

A type may be assigned to TableCell, by setting the type property. Beyond DEFAULT, here are the most notable types:

  • NUMERIC: Use this for numeric vales like currencies, dates, percentages, etc. Cells are right-aligned by default, and tabular numbers will be used when displaying digits.
  • ACTIONS: Use this for cells that contain the so-called “action” buttons at the end of a row. Cells are right-aligned by default, and the buttons’ vertical alignment will be adjusted.
  • DETAILS: Cell content will be displayed in full width underneath the other columns.
  • COLLAPSIBLE: Automatically assigned when using CollapsibleTableCell, which is the collapsible part of a row in CollapsibleTable. Much like DETAILS, the cell content is once again displayed in full width underneath the other columns. Hidden by default, this type is only shown when the table row is expanded.

The last two types (DETAILS and COLLAPSIBLE) are only allowed in complex tables. They are meant to be used either-or, a table should not contain both.

HINT The “Table with complex data” example in Storybook showcases NUMERIC (date), ACTIONS (buttons) and DETAILS (Alert, last row only).

Width

Use the width property to assign a width to a column. Numeric values are treated as pixels, but we recommend to use strings, either an actual value like <n>rem, or a percentage such as <n>%.

In non-complex tables, i.e. those with layout set to DEFAULT or FIXED, you can get by without specifying a column width at all. Table cells will always be placed on one row, and each width is merely a suggestion because columns shrink/grow to end up full width across tiers.

In complex tables, you must set a width for your columns explicitly, otherwise, the table-like appearance could break whenever your cell content grows too large. Contrary to non-complex tables, a specified column width is treated as-is, i.e. columns do not shrink/grow.

Rules For Developers
  • Non-complex tables derive the way to come up with the actual column widths from regular HTML tables – well, CSS’s display: table and table-layout: fixed, to be exact.
  • Complex tables are based on CSS’s flexbox display behaviour. If the sum of the widths of all columns goes beyond the available total width on any given tier, one or more columns may end up underneath the previous ones, which is not desired. Your best bet is to use percentages for every column. Exception: Columns of type DETAILS and COLLAPSIBLE never need a width.

Alignment

A TableCell can be aligned by setting the alignment property to AUTO, LEFT, CENTER, or RIGHT. The default AUTO means cells are aligned according to their type. Without a specific type, cells are left-aligned, unless they are last in each row, which makes them right-aligned. “Last” does not necessarily mean last in source order, but the visually last one: E.g., if the last cell is of type DETAILS, which is displayed underneath all the other columns, then the one before it becomes “the last one” visually.

There is also a boolean property named center, which when set centre-aligns cell content vertically.

Truncation

A TableCell can be truncated by setting the truncate property to true.

The additional header truncation property truncateHeaders – which is a property of Table, not TableCell – is by default set to AUTO. In this case the column headers adhere to the same truncation logic as their content below. To achieve a more unified look, you can change it to ALWAYS (truncate all headers) or NEVER (wrap all headers) instead.

CollapsibleTable

Open in Storybook

CollapsibleTable is able to expand rows to reveal more content.

Loading demo…