Porsche Design SystemSearchNavigate to GitHub repository of Porsche Design SystemOpen sidebar
ConfiguratorExamplesUsageAccessibilityAPI
Table Table of Contents Caption A caption that describes the content of the table can be set in two ways. It's mandatory to define a descriptive caption to fulfill accessibility criteria. Via property Using the caption property doesn't display the caption but instead can be used to improve accessibility of the table.
Column 1Column 2Column 3Cell 1Cell 2Cell 3
Open in Stackblitz
<!doctype html>
<html lang="en" class="auto">
<head>
  <title></title>
</head>
<body class="bg-base">

<p-table caption="Some caption">
  <p-table-head>
    <p-table-head-row>
      <p-table-head-cell>
        Column 1
      </p-table-head-cell>
      <p-table-head-cell>
        Column 2
      </p-table-head-cell>
      <p-table-head-cell>
        Column 3
      </p-table-head-cell>
    </p-table-head-row>
  </p-table-head>
  <p-table-body>
    <p-table-row>
      <p-table-cell>
        Cell 1
      </p-table-cell>
      <p-table-cell>
        Cell 2
      </p-table-cell>
      <p-table-cell>
        Cell 3
      </p-table-cell>
    </p-table-row>
  </p-table-body>
</p-table>
<script>

</script>
</body>
</html>
Via slot When using the caption slot its content will be rendered while offering full control of appearance.
Some slotted captionColumn 1Column 2Column 3Cell 1Cell 2Cell 3
Open in Stackblitz
<!doctype html>
<html lang="en" class="auto">
<head>
  <title></title>
</head>
<body class="bg-base">

<p-table>
  <p-heading slot="caption" size="large">
    Some slotted caption
  </p-heading>
  <p-table-head>
    <p-table-head-row>
      <p-table-head-cell>
        Column 1
      </p-table-head-cell>
      <p-table-head-cell>
        Column 2
      </p-table-head-cell>
      <p-table-head-cell>
        Column 3
      </p-table-head-cell>
    </p-table-head-row>
  </p-table-head>
  <p-table-body>
    <p-table-row>
      <p-table-cell>
        Cell 1
      </p-table-cell>
      <p-table-cell>
        Cell 2
      </p-table-cell>
      <p-table-cell>
        Cell 3
      </p-table-cell>
    </p-table-row>
  </p-table-body>
</p-table>
<script>

</script>
</body>
</html>
Layout: fixed By setting layout to fixed you can get full control over every column width that are otherwise controlled by their content. An identical width, min-width and/or max-width depending on what you want to achieve, has to be specified on every p-table-head-cell and p-table-cell within the same column. While it is possible to use relative width units likes 50% or 50vw, these may lead to unexpected results when the table is scrollable especially on smaller screens or when combined with absolute (e.g. 50px) or default auto values (based on content). If there is more content than available space, it will be overflowing, which you need to take care of, e.g. by using <p-text ellipsis>Some content</p-text>. Since truncated content cannot be fully read, it is important to provide an alternative like a title attribute or custom tooltip.
Column 1 (50%)Column 2 (150px)Column 3 (auto)Cell 1Cell 2Cell 3Cell 1Cell 2 with more contentCell 3
Open in Stackblitz
<!doctype html>
<html lang="en" class="auto">
<head>
  <title></title>
</head>
<body class="bg-base">

<p-table caption="Some caption" layout="fixed">
  <p-table-head>
    <p-table-head-row>
      <p-table-head-cell class="w-[50%] max-w-[50%]">
        Column 1 (50%)
      </p-table-head-cell>
      <p-table-head-cell class="w-[150px] max-w-[150px]">
        Column 2 (150px)
      </p-table-head-cell>
      <p-table-head-cell>
        Column 3 (auto)
      </p-table-head-cell>
    </p-table-head-row>
  </p-table-head>
  <p-table-body>
    <p-table-row>
      <p-table-cell class="w-[50%] max-w-[50%]">
        Cell 1
      </p-table-cell>
      <p-table-cell class="w-[150px] max-w-[150px]">
        Cell 2
      </p-table-cell>
      <p-table-cell>
        Cell 3
      </p-table-cell>
    </p-table-row>
    <p-table-row>
      <p-table-cell class="w-[50%] max-w-[50%]">
        Cell 1
      </p-table-cell>
      <p-table-cell class="w-[150px] max-w-[150px]">
        <p-text ellipsis="true" title="Cell 2 with more content">
          Cell 2 with more content
        </p-text>
      </p-table-cell>
      <p-table-cell>
        Cell 3
      </p-table-cell>
    </p-table-row>
  </p-table-body>
</p-table>
<script>

</script>
</body>
</html>
Column Headers The p-table's head can be configured by setting one or more of the following properties on each p-table-head-cell. Sorting In order to have a sortable table column you need to provide the sort property. It has the following structure:
type TableHeadCellSort = { id: string; // identifier for the column to be sorted by active: boolean; direction: 'asc' | 'desc'; };
Upon clicking a sortable p-table-head-cell element, the p-table emits an update event that you should subscribe to. The sortingChange event has been deprecated and will be removed with the next major release.
Please use the update event instead.
Column 1Column 2Column 3Name A901.06.2021Name Z124.06.2021
Open in Stackblitz
<!doctype html>
<html lang="en" class="auto">
<head>
  <title></title>
</head>
<body class="bg-base">

<p-table caption="Some caption"></p-table>
<script>
  (() => {
    const headSorting = [
      { name: 'Column 1', id: 'col1' },
      { name: 'Column 2', id: 'col2' },
      { name: 'Column 3', id: 'col3' },
    ];

    const dataSorting = [
      {
        col1: 'Name A',
        col2: '9',
        col3: '01.06.2021',
      },
      {
        col1: 'Name Z',
        col2: '1',
        col3: '24.06.2021',
      },
    ];

    const renderTableHeadRow = (items) =>
      [
        '<p-table-head-row>',
        ...items.map((item) => `<p-table-head-cell>${item.name}</p-table-head-cell>`),
        '</p-table-head-row>',
      ].join('');

    const renderTableBodyRows = (items) =>
      items
        .map(
          (item) => `
  <p-table-row>
    <p-table-cell>${item.col1}</p-table-cell>
    <p-table-cell>${item.col2}</p-table-cell>
    <p-table-cell>${item.col3}</p-table-cell>
  </p-table-row>`
        )
        .join('');

    const markup = `
  <p-table-head>${renderTableHeadRow(headSorting)}</p-table-head>
  <p-table-body>${renderTableBodyRows(dataSorting)}</p-table-body>`;

    const table = document.querySelector('p-table');
    table.innerHTML = markup;
    const tableHeadCells = table.querySelectorAll('p-table-head-cell');
    const tableBody = table.querySelector('p-table-body');

    tableHeadCells.forEach((cell, index) => {
      cell.sort = { id: index.toString(), active: false, direction: 'asc' };
    });

    table.addEventListener('update', (e) => {
      const { id, direction } = e.detail;

      tableHeadCells.forEach((cell, index) => {
        cell.sort = {
          id: index.toString(),
          active: index === Number(id),
          direction: index === Number(id) ? direction : 'asc',
        };
      });

      const rows = Array.from(tableBody.querySelectorAll('p-table-row'));

      const sortedRows = rows.sort((a, b) => {
        const aText = a.querySelectorAll('p-table-cell')[id].textContent.trim();
        const bText = b.querySelectorAll('p-table-cell')[id].textContent.trim();
        const compare = aText.localeCompare(bText);
        return direction === 'asc' ? compare : -compare;
      });

      sortedRows.forEach(row => tableBody.appendChild(row));
    });
  })();
</script>
</body>
</html>
Hide Label Sometimes you want to hide the label of a table column for example when the column's content is self-explanatory. This can be achieved by setting the hide-label property.
Column 1Column 2Column 3Cell 1Cell 2Cell 3
Open in Stackblitz
<!doctype html>
<html lang="en" class="auto">
<head>
  <title></title>
</head>
<body class="bg-base">

<p-table caption="Some caption">
  <p-table-head>
    <p-table-head-row>
      <p-table-head-cell>
        Column 1
      </p-table-head-cell>
      <p-table-head-cell>
        Column 2
      </p-table-head-cell>
      <p-table-head-cell hide-label="true">
        Column 3
      </p-table-head-cell>
    </p-table-head-row>
  </p-table-head>
  <p-table-body>
    <p-table-row>
      <p-table-cell>
        Cell 1
      </p-table-cell>
      <p-table-cell>
        Cell 2
      </p-table-cell>
      <p-table-cell>
        Cell 3
      </p-table-cell>
    </p-table-row>
  </p-table-body>
</p-table>
<script>

</script>
</body>
</html>
Advanced Table The appearance of a table's contents can be customized as illustrated in the following example.
Some visual captionModelInterestVINPurchase IntentionStatusCommentLead IDSelect WrapperSelectMulti-SelectAction
71823.06.2021
New Car1FM5K7F84FGB1630408/2021Won-0000824402EditDelete
Panamera19.06.2021
New Car2GCEC13T14137480111/2021LostSome multiline text and a column with a min width.0000824409EditDelete
91119.05.2021
Used Car5GAKVCKD8EJ33575009/2021Won-0000824408EditDelete
Macan10.05.2021
Used Car1FMPU17L83LC0930207/2021Lost-0000824407EditDelete
Taycan03.05.2021
New CarJN1BY1AR3BM37518705/2021Won-0000824406EditDelete
Open in Stackblitz
<!doctype html>
<html lang="en" class="auto">
<head>
  <title></title>
</head>
<body class="bg-base">

<p-table>
  <p-heading slot="caption" variant="large" tag="h3">Some visual caption</p-heading>
  <p-table-head></p-table-head>
  <p-table-body></p-table-body>
</p-table>
<script>
  (() => {
    const headAdvanced = [
      { name: 'Model', id: 'model' },
      { name: 'Interest', id: 'interest' },
      { name: 'VIN', id: 'vin' },
      { name: 'Purchase Intention', id: 'purchaseIntention' },
      { name: 'Status', id: 'status' },
      { name: 'Comment', id: 'comment' },
      { name: 'Lead ID', id: 'leadId' },
      { name: 'Select Wrapper', id: 'selectWrapper' },
      { name: 'Select', id: 'select' },
      { name: 'Multi-Select', id: 'multiSelect' },
      { name: 'Action', id: 'action', hideLabel: true },
    ].map((item, i) => ({
      ...item,
      ...(i > 0 &&
        i < 7 &&
        i !== 5 && {
          active: i === 1,
          direction: 'asc',
        }),
    }));
    const dataAdvanced = [
      {
        imageUrl: 'assets/718.png',
        model: '718',
        date: '23.06.2021',
        interest: 'New Car',
        vin: '1FM5K7F84FGB16304',
        purchaseIntention: '08/2021',
        status: 'Won',
        comment: '-',
        leadId: '0000824402',
      },
      {
        imageUrl: 'assets/panamera.png',
        model: 'Panamera',
        date: '19.06.2021',
        interest: 'New Car',
        vin: '2GCEC13T141374801',
        purchaseIntention: '11/2021',
        status: 'Lost',
        comment: 'Some multiline text and a column with a min width.',
        leadId: '0000824409',
      },
      {
        imageUrl: 'assets/911.png',
        model: '911',
        date: '19.05.2021',
        interest: 'Used Car',
        vin: '5GAKVCKD8EJ335750',
        purchaseIntention: '09/2021',
        status: 'Won',
        comment: '-',
        leadId: '0000824408',
      },
      {
        imageUrl: 'assets/macan.png',
        model: 'Macan',
        date: '10.05.2021',
        interest: 'Used Car',
        vin: '1FMPU17L83LC09302',
        purchaseIntention: '07/2021',
        status: 'Lost',
        comment: '-',
        leadId: '0000824407',
      },
      {
        imageUrl: 'assets/taycan.png',
        model: 'Taycan',
        date: '03.05.2021',
        interest: 'New Car',
        vin: 'JN1BY1AR3BM375187',
        purchaseIntention: '05/2021',
        status: 'Won',
        comment: '-',
        leadId: '0000824406',
      },
    ];

    const renderTableHeadRow = (items) =>
      [
        '<p-table-head-row>',
        ...items.map((item) => `<p-table-head-cell>${item.name || ''}</p-table-head-cell>`),
        '</p-table-head-row>',
      ].join('');

    const renderTableBodyRows = (items) =>
      items
        .map(
          (item) => `
<p-table-row>
  <p-table-cell>
    <div style="display: flex;">
      <img src="${item.imageUrl}" width="80" height="45" style="margin-right: .5rem; object-fit: contain; max-width: none;" alt="" />
      <div>
        <p-text weight='semi-bold'>${item.model}</p-text>
        <p-text size='x-small'>${item.date}</p-text>
      </div>
    </div>
  </p-table-cell>
  <p-table-cell>${item.interest}</p-table-cell>
  <p-table-cell><a href="https://porsche.com">${item.vin}</a></p-table-cell>
  <p-table-cell>${item.purchaseIntention}</p-table-cell>
  <p-table-cell>${item.status}</p-table-cell>
  <p-table-cell multiline="true" style="min-width: 10rem;">${item.comment}</p-table-cell>
  <p-table-cell>${item.leadId} <p-popover description="Some additional content."></p-popover></p-table-cell>
  <p-table-cell>
    <p-select-wrapper label="Select Something" style="min-width: 160px;">
      <select name="some-name">
        <option value="a">Option A</option>
        <option value="b">Option B</option>
        <option value="c">Option C</option>
        <option value="d">Option D</option>
        <option value="e">Option E</option>
        <option value="f">Option F</option>
        <option value="g">Option G</option>
        <option value="h">Option H</option>
        <option value="i">Option I</option>
        <option value="j">Option J</option>
        <option value="k">Option K</option>
      </select>
    </p-select-wrapper>
  </p-table-cell>
  <p-table-cell>
    <p-select name="options" label="Select Something" style="min-width: 160px; display: block;">
      <p-select-option value="a">Option A</p-select-option>
      <p-select-option value="b">Option B</p-select-option>
      <p-select-option value="c">Option C</p-select-option>
      <p-select-option value="d">Option D</p-select-option>
      <p-select-option value="e">Option E</p-select-option>
      <p-select-option value="f">Option F</p-select-option>
      <p-select-option value="g">Option G</p-select-option>
      <p-select-option value="h">Option H</p-select-option>
      <p-select-option value="i">Option I</p-select-option>
      <p-select-option value="j">Option J</p-select-option>
      <p-select-option value="k">Option K</p-select-option>
    </p-select>
  </p-table-cell>
  <p-table-cell>
    <p-multi-select name="name" label="Multi-Select Something" style="min-width: 160px;">
      <p-multi-select-option value="a">Option A</p-multi-select-option>
      <p-multi-select-option value="b">Option B</p-multi-select-option>
      <p-multi-select-option value="c">Option C</p-multi-select-option>
      <p-multi-select-option value="d">Option D</p-multi-select-option>
      <p-multi-select-option value="e">Option E</p-multi-select-option>
      <p-multi-select-option value="f">Option F</p-multi-select-option>
      <p-multi-select-option value="g">Option G</p-multi-select-option>
      <p-multi-select-option value="h">Option H</p-multi-select-option>
      <p-multi-select-option value="i">Option I</p-multi-select-option>
      <p-multi-select-option value="j">Option J</p-multi-select-option>
      <p-multi-select-option value="k">Option K</p-multi-select-option>
    </p-multi-select>
  </p-table-cell>
  <p-table-cell>
    <p-button-pure icon="edit" style="padding: .5rem">Edit</p-button-pure>
    <p-button-pure icon="delete" style="padding: .5rem">Delete</p-button-pure>
  </p-table-cell>
</p-table-row>`
        )
        .join('');

    const table = document.querySelector('p-table');
    const tableHead = table.querySelector('p-table-head');
    tableHead.innerHTML = renderTableHeadRow(headAdvanced);
    const tableBody = table.querySelector('p-table-body');
    tableBody.innerHTML = renderTableBodyRows(dataAdvanced);

    const tableHeadCells = table.querySelectorAll('p-table-head-cell');

    tableHeadCells.forEach((cell, index) => {
      cell.sort = { id: index.toString(), active: false, direction: 'asc' };
    });

    table.addEventListener('update', (e) => {
      const { id, direction } = e.detail;

      tableHeadCells.forEach((cell, index) => {
        cell.sort = {
          id: index.toString(),
          active: index === Number(id),
          direction: index === Number(id) ? direction : 'asc',
        };
      });

      const rows = Array.from(tableBody.querySelectorAll('p-table-row'));

      const sortedRows = rows.sort((a, b) => {
        const aText = a.querySelectorAll('p-table-cell')[id].textContent.trim();
        const bText = b.querySelectorAll('p-table-cell')[id].textContent.trim();
        const compare = aText.localeCompare(bText);
        return direction === 'asc' ? compare : -compare;
      });

      sortedRows.forEach(row => tableBody.appendChild(row));
    });
  })();
</script>
</body>
</html>
Global settingsThemeChanges the theme of the application and any Porsche Design System component. It's possible to choose between forced theme light and dark. It's also possible to use auto, which applies light or dark theme depending on the operating system settings automatically.LightDarkAuto (sync with operating system)DirectionChanges the direction of HTML elements, mostly used on<html> tag to support languages which are read from right to left like e.g. Arabic.LTR (left-to-right)RTL (right-to-left)AutoText ZoomChanges the text size and values with unit rem or em relatively. This setting can be defined in browser settings for any website or by an application itself on<html> tag. To achieve WCAG 2.2 AA compliance it's obligatory to support text zoom up to at least 200%.100%130%150%200%