Content presentation - Table

Use a table to make it easier for users to compare and scan information.

Basic tables

2 column table

Open this basic table example in a new tab
Copy basic table code
<table class="nhsuk-table">
  <caption class="nhsuk-table__caption">Skin symptoms and possible causes</caption>
  <thead role="rowgroup" class="nhsuk-table__head">
    <tr role="row">
      <th role="columnheader" class="" scope="col">
        Skin symptoms
      </th>
      <th role="columnheader" class="" scope="col">
        Possible cause
      </th>
    </tr>
  </thead>
  <tbody class="nhsuk-table__body">
    <tr role="row" class="nhsuk-table__row">
      <td class="nhsuk-table__cell">Blisters on lips or around the mouth</td>
      <td class="nhsuk-table__cell ">Cold sores</td>
    </tr>
    <tr role="row" class="nhsuk-table__row">
      <td class="nhsuk-table__cell">Itchy, dry, cracked, sore</td>
      <td class="nhsuk-table__cell ">Eczema</td>
    </tr>
    <tr role="row" class="nhsuk-table__row">
      <td class="nhsuk-table__cell">Itchy blisters</td>
      <td class="nhsuk-table__cell ">Shingles, chickenpox</td>
    </tr>
  </tbody>
</table>
Close basic table code
Nunjucks macro options

Use options to customise the appearance, content and behaviour of a component when using a macro, for example, changing the text.

Some options are required for the macro to work; these are marked as "Required" in the option description.

If you're using Nunjucks macros in production with "html" options, or ones ending with "html", you must sanitise the HTML to protect against cross-site scripting exploits.

Nunjucks arguments for basic table
Name Type Required Description
rows array true Array of table rows and cells.
rows[].text string true If `html` is set, this is not required. Text for cells in table rows. If `html` is provided, the `text` argument will be ignored.
rows[].html string true If `text` is set, this is not required. HTML for cells in table rows. If `html` is provided, the `text` argument will be ignored.
rows[].format string false Specify format of a cell. Currently we only use "numeric".
rows[].colspan integer false Specify how many columns a cell extends.
rows[].rowspan integer false Specify how many rows a cell extends.
head array false Array of table head cells.
head[].text string false If `html` is set, this is not required. Text for table head cells. If `html` is provided, the `text` argument will be ignored.
head[].html string false If `text` is set, this is not required. HTML for table head cells. If `html` is provided, the `text` argument will be ignored.
head[].format string false Specify format of a cell. Currently we only use "numeric".
head[].colspan integer false Specify how many columns a cell extends.
head[].rowspan integer false Specify how many rows a cell extends.
heading string false Heading/label of the panel if the panel argument is set to true.
headingLevel integer false Optional heading level for the heading. Default: 3.
caption string false Caption text.
captionClasses string false Classes for caption text size. Classes to add to the table caption, for example `nhsuk-table__caption--l`.
firstCellIsHeader boolean false If set to true, first cell in table row will be a TH instead of a TD.
responsive boolean false If set to true, responsive table classes will be applied.
tableClasses string false Classes to add to the table container.
attributes object false HTML attributes (for example data attributes) to add to the table container.
Copy basic table code
{% from 'tables/macro.njk' import table %}

{{ table({
  panel: false,
  caption: "Skin symptoms and possible causes",
  firstCellIsHeader: false,
  head: [
    {
      text: "Skin symptoms"
    },
    {
      text: "Possible cause"
    }
  ],
  rows: [
    [
      {
        text: "Blisters on lips or around the mouth"
      },
      {
        text: "Cold sores"
      }
    ],
    [
      {
        text: "Itchy, dry, cracked, sore"
      },
      {
        text: "Eczema"
      }
    ],
    [
      {
        text: "Itchy blisters"
      },
      {
        text: "Shingles, chickenpox"
      }
    ]
  ]
}) }}
Close basic table code

3 or more column table

If you have numeric data, right align the column header and cells to make it easier to compare numbers.

Open this 3 column table example in a new tab
Copy 3 column table code
<table class="nhsuk-table">
  <caption class="nhsuk-table__caption">Prescription prepayment certificate (PPC) charges</caption>
  <thead role="rowgroup" class="nhsuk-table__head">
    <tr role="row">
      <th role="columnheader" class="" scope="col">
        Item
      </th>
      <th role="columnheader" class=" nhsuk-table__header--numeric" scope="col">
        Current charge
      </th>
      <th role="columnheader" class=" nhsuk-table__header--numeric" scope="col">
        New charge
      </th>
    </tr>
  </thead>
  <tbody class="nhsuk-table__body">
    <tr role="row" class="nhsuk-table__row">
      <th class="nhsuk-table__header" scope="row">3-month</th>
      <td class="nhsuk-table__cell nhsuk-table__cell--numeric">£31.25</td>
      <td class="nhsuk-table__cell nhsuk-table__cell--numeric">£32.05</td>
    </tr>
    <tr role="row" class="nhsuk-table__row">
      <th class="nhsuk-table__header" scope="row">12-month</th>
      <td class="nhsuk-table__cell nhsuk-table__cell--numeric">£111.60</td>
      <td class="nhsuk-table__cell nhsuk-table__cell--numeric">£114.50</td>
    </tr>
    <tr role="row" class="nhsuk-table__row">
      <th class="nhsuk-table__header" scope="row">HRT</th>
      <td class="nhsuk-table__cell nhsuk-table__cell--numeric">£19.30</td>
      <td class="nhsuk-table__cell nhsuk-table__cell--numeric">£19.80</td>
    </tr>
  </tbody>
</table>
Close 3 column table code
Nunjucks macro options

Use options to customise the appearance, content and behaviour of a component when using a macro, for example, changing the text.

Some options are required for the macro to work; these are marked as "Required" in the option description.

If you're using Nunjucks macros in production with "html" options, or ones ending with "html", you must sanitise the HTML to protect against cross-site scripting exploits.

Nunjucks arguments for 3 column table
Name Type Required Description
rows array true Array of table rows and cells.
rows[].text string true If `html` is set, this is not required. Text for cells in table rows. If `html` is provided, the `text` argument will be ignored.
rows[].html string true If `text` is set, this is not required. HTML for cells in table rows. If `html` is provided, the `text` argument will be ignored.
rows[].format string false Specify format of a cell. Currently we only use "numeric".
rows[].colspan integer false Specify how many columns a cell extends.
rows[].rowspan integer false Specify how many rows a cell extends.
head array false Array of table head cells.
head[].text string false If `html` is set, this is not required. Text for table head cells. If `html` is provided, the `text` argument will be ignored.
head[].html string false If `text` is set, this is not required. HTML for table head cells. If `html` is provided, the `text` argument will be ignored.
head[].format string false Specify format of a cell. Currently we only use "numeric".
head[].colspan integer false Specify how many columns a cell extends.
head[].rowspan integer false Specify how many rows a cell extends.
heading string false Heading/label of the panel if the panel argument is set to true.
headingLevel integer false Optional heading level for the heading. Default: 3.
caption string false Caption text.
captionClasses string false Classes for caption text size. Classes to add to the table caption, for example `nhsuk-table__caption--l`.
firstCellIsHeader boolean false If set to true, first cell in table row will be a TH instead of a TD.
responsive boolean false If set to true, responsive table classes will be applied.
tableClasses string false Classes to add to the table container.
attributes object false HTML attributes (for example data attributes) to add to the table container.
Copy 3 column table code
{% from 'tables/macro.njk' import table %}

{{ table({
  responsive: false,
  panel: false,
  caption: "Prescription prepayment certificate (PPC) charges",
  firstCellIsHeader: true,
  head: [
    {
      text: "Item"
    },
    {
      text: "Current charge",
      format: "numeric"
    },
    {
      text: "New charge",
      format: "numeric"
    }
  ],
  rows: [
    [
      {
        text: "3-month"
      },
      {
        text: "£31.25",
        format: "numeric"
      },
      {
        text: "£32.05",
        format: "numeric"
      }
    ],
    [
      {
        text: "12-month"
      },
      {
        text: "£111.60",
        format: "numeric"
      },
      {
        text: "£114.50",
        format: "numeric"
      }
    ],
    [
      {
        text: "HRT"
      },
      {
        text: "£19.30",
        format: "numeric"
      },
      {
        text: "£19.80",
        format: "numeric"
      }
    ]
  ]
}) }}
Close 3 column table code

When not to use a basic table

Do not use a basic table:

  • to lay out content on a page – use the grid system instead
  • if your table content becomes squashed and hard to read on small screens – use a responsive table instead

Responsive table

This table has a responsive layout. On large screens it displays as a 3 column table. However, on small screens it stacks vertically.

Use a responsive table when your table becomes squashed and hard to read on small screens, 768px and smaller.

Open this responsive table example in a new tab
Copy responsive table code
<table role="table" class="nhsuk-table-responsive">
  <caption class="nhsuk-table__caption">Ibuprofen liquid dosages for children</caption>
  <thead role="rowgroup" class="nhsuk-table__head">
    <tr role="row">
      <th role="columnheader" class="" scope="col">
        Age
      </th>
      <th role="columnheader" class="" scope="col">
        How much
      </th>
      <th role="columnheader" class="" scope="col">
        How often
      </th>
    </tr>
  </thead>
  <tbody class="nhsuk-table__body">
    <tr role="row" class="nhsuk-table__row">
      <td role="cell" class="nhsuk-table__cell">
        <span class="nhsuk-table-responsive__heading" aria-hidden="true">Age </span>4 to 6 years
      </td>
      <td role="cell" class="nhsuk-table__cell">
        <span class="nhsuk-table-responsive__heading" aria-hidden="true">How much </span>7.5ml (150mg)
      </td>
      <td role="cell" class="nhsuk-table__cell">
        <span class="nhsuk-table-responsive__heading" aria-hidden="true">How often </span>Max 3 times in 24 hours
      </td>
    </tr>
    <tr role="row" class="nhsuk-table__row">
      <td role="cell" class="nhsuk-table__cell">
        <span class="nhsuk-table-responsive__heading" aria-hidden="true">Age </span>7 to 9 years
      </td>
      <td role="cell" class="nhsuk-table__cell">
        <span class="nhsuk-table-responsive__heading" aria-hidden="true">How much </span>10ml (200mg)
      </td>
      <td role="cell" class="nhsuk-table__cell">
        <span class="nhsuk-table-responsive__heading" aria-hidden="true">How often </span>Max 3 times in 24 hours
      </td>
    </tr>
    <tr role="row" class="nhsuk-table__row">
      <td role="cell" class="nhsuk-table__cell">
        <span class="nhsuk-table-responsive__heading" aria-hidden="true">Age </span>10 to 11 years
      </td>
      <td role="cell" class="nhsuk-table__cell">
        <span class="nhsuk-table-responsive__heading" aria-hidden="true">How much </span>15ml (300mg)
      </td>
      <td role="cell" class="nhsuk-table__cell">
        <span class="nhsuk-table-responsive__heading" aria-hidden="true">How often </span>Max 3 times in 24 hours
      </td>
    </tr>
  </tbody>
</table>
Close responsive table code
Nunjucks macro options

Use options to customise the appearance, content and behaviour of a component when using a macro, for example, changing the text.

Some options are required for the macro to work; these are marked as "Required" in the option description.

If you're using Nunjucks macros in production with "html" options, or ones ending with "html", you must sanitise the HTML to protect against cross-site scripting exploits.

Nunjucks arguments for responsive table
Name Type Required Description
rows array true Array of table rows and cells.
rows[].text string true If `html` is set, this is not required. Text for cells in table rows. If `html` is provided, the `text` argument will be ignored.
rows[].html string true If `text` is set, this is not required. HTML for cells in table rows. If `html` is provided, the `text` argument will be ignored.
rows[].format string false Specify format of a cell. Currently we only use "numeric".
rows[].colspan integer false Specify how many columns a cell extends.
rows[].rowspan integer false Specify how many rows a cell extends.
head array false Array of table head cells.
head[].text string false If `html` is set, this is not required. Text for table head cells. If `html` is provided, the `text` argument will be ignored.
head[].html string false If `text` is set, this is not required. HTML for table head cells. If `html` is provided, the `text` argument will be ignored.
head[].format string false Specify format of a cell. Currently we only use "numeric".
head[].colspan integer false Specify how many columns a cell extends.
head[].rowspan integer false Specify how many rows a cell extends.
heading string false Heading/label of the panel if the panel argument is set to true.
headingLevel integer false Optional heading level for the heading. Default: 3.
caption string false Caption text.
captionClasses string false Classes for caption text size. Classes to add to the table caption, for example `nhsuk-table__caption--l`.
firstCellIsHeader boolean false If set to true, first cell in table row will be a TH instead of a TD.
responsive boolean false If set to true, responsive table classes will be applied.
tableClasses string false Classes to add to the table container.
attributes object false HTML attributes (for example data attributes) to add to the table container.
Copy responsive table code
{% from 'tables/macro.njk' import table %}

{{ table({
  responsive: true,
  panel: false,
  caption: "Ibuprofen liquid dosages for children",
  firstCellIsHeader: true,
  head: [
    {
      text: "Age"
    },
    {
      text: "How much"
    },
    {
      text: "How often"
    }
  ],
  rows: [
    [
      {
        header: "Age",
        text: "4 to 6 years"
      },
      {
        header: "How much",
        text: "7.5ml (150mg)"
      },
      {
        header: "How often",
        text: "Max 3 times in 24 hours"
      }
    ],
    [
      {
        header: "Age",
        text: "7 to 9 years"
      },
      {
        header: "How much",
        text: "10ml (200mg)"
      },
      {
        header: "How often",
        text: "Max 3 times in 24 hours"
      }
    ],
        [
      {
        header: "Age",
        text: "10 to 11 years"
      },
      {
        header: "How much",
        text: "15ml (300mg)"
      },
      {
        header: "How often",
        text: "Max 3 times in 24 hours"
      }
    ]
  ]
}) }}
Close responsive table code

When not to use a responsive table

Do not use a responsive table:

  • to lay out content on a page – use the grid system instead
  • if your content is easier to read and understand on a small screen in a non-responsive layout – use a basic table instead

How tables work

Accessibility

Follow WebAIM's guidance for tables and:

  • give tables captions
  • use the scope attribute to associate the data cells with the appropriate headers
  • let the browser window determine the width of the table whenever possible, to reduce horizontal scrolling

Table captions

Use the <caption> element to describe a table in the same way you would use a heading. A caption helps users find, navigate and understand tables.

Table headers

Use table headers to tell users what the rows and columns represent.

Research

Basic tables

These tables tested well with users of health information on the NHS website.

Responsive table

This table was tested at HM Revenue & Customs.

Table with panel

We have also designed a table with a panel. You can find an example in the frontend library. We have not included it in this list of components yet because it needs more testing.

Help us improve this guidance

Share insights or feedback and take part in the discussion. We use GitHub as a collaboration space. All the information on it is open to the public.

Read more about how to feedback or share insights.

If you have any questions, get in touch with the service manual team.

Updated: July 2024