Back to all examples

Sort data in a table

interface User {
  firstName: string;
  lastName: string;
  age: number;
}

const [users, setUsers] = useState<User[]>([]);

  useEffect(() => {
    const _users: User[] = [
      { firstName: "Christian", lastName: "Batz", age: 18 },
      { firstName: "Brain", lastName: "Wisozk", age: 19 },
      { firstName: "Neha", lastName: "Jones", age: 23 },
      { firstName: "Tristin", lastName: "Buckridge", age: 31 },
    ];
    setUsers(_users);
  }, []);

  function sortData(event: GoabTableOnSortDetail) {
    const _users = [...users];
    _users.sort((a: any, b: any) => {
<GoabxTable onSort={sortData} width="100%">
      <thead>
        <tr>
          <th>
            <GoabxTableSortHeader name="firstName">First name</GoabxTableSortHeader>
          </th>
          <th>
            <GoabxTableSortHeader name="lastName">Last name</GoabxTableSortHeader>
          </th>
          <th>
            <GoabxTableSortHeader name="age" direction="asc">
              Age
            </GoabxTableSortHeader>
          </th>
        </tr>
      </thead>
      <tbody>
        {users.map((user) => (
          <tr key={user.firstName}>
            <td>{user.firstName}</td>
            <td>{user.lastName}</td>
            <td>{user.age}</td>
          </tr>
        ))}
      </tbody>
    </GoabxTable>
users: User[] = [
    { firstName: "Christian", lastName: "Batz", age: 18 },
    { firstName: "Brain", lastName: "Wisozk", age: 19 },
    { firstName: "Neha", lastName: "Jones", age: 23 },
    { firstName: "Tristin", lastName: "Buckridge", age: 31 },
  ];

  handleSort(event: GoabTableOnSortDetail): void {
    const { sortBy, sortDir } = event;
    this.users.sort(
      (a: any, b: any) => (a[sortBy] > b[sortBy] ? 1 : -1) * sortDir
    );
  }
<goabx-table width="100%" mb="xl" (onSort)="handleSort($event)">
  <thead>
    <tr>
      <th>
        <goab-table-sort-header name="firstName">First name</goab-table-sort-header>
      </th>
      <th>
        <goab-table-sort-header name="lastName">Last name</goab-table-sort-header>
      </th>
      <th>
        <goab-table-sort-header name="age" direction="asc">Age</goab-table-sort-header>
      </th>
    </tr>
  </thead>
  <tbody>
    <tr *ngFor="let user of users">
      <td>{{ user.firstName }}</td>
      <td>{{ user.lastName }}</td>
      <td>{{ user.age }}</td>
    </tr>
  </tbody>
</goabx-table>
let users = [
  { firstName: "Christian", lastName: "Batz", age: 18 },
  { firstName: "Brain", lastName: "Wisozk", age: 19 },
  { firstName: "Neha", lastName: "Jones", age: 23 },
  { firstName: "Tristin", lastName: "Buckridge", age: 31 },
];

const tbody = document.getElementById("table-body");
let sortColumn = "age";
let sortDirection = 1; // 1 = asc, -1 = desc

function renderTable() {
  tbody.innerHTML = "";
  users.forEach((user) => {
    const row = document.createElement("tr");
    row.innerHTML = `
      <td>${user.firstName}</td>
      <td>${user.lastName}</td>
      <td>${user.age}</td>
    `;
    tbody.appendChild(row);
  });
}

function sortTable(column) {
  // Toggle direction if same column, otherwise start ascending
  if (column === sortColumn) {
    sortDirection = sortDirection * -1;
  } else {
    sortColumn = column;
    sortDirection = 1;
  }

  // Update header visual states
  document.querySelectorAll("goa-table-sort-header").forEach((header) => {
    const name = header.getAttribute("name");
    if (name === sortColumn) {
      header.setAttribute("direction", sortDirection === 1 ? "asc" : "desc");
    } else {
      header.setAttribute("direction", "none");
    }
  });

  // Sort the data
  users.sort((a, b) => (a[sortColumn] > b[sortColumn] ? 1 : -1) * sortDirection);
  renderTable();
}

// Attach click handlers directly to sort headers
// (Table's built-in slot-based wiring doesn't work with innerHTML)
document.querySelectorAll("goa-table-sort-header").forEach((header) => {
  header.addEventListener("click", () => {
    const column = header.getAttribute("name");
    sortTable(column);
  });
});

// Initial sort by age ascending
users.sort((a, b) => (a.age > b.age ? 1 : -1));
renderTable();
<goa-table version="2" width="100%" mb="xl">
  <table width="100%">
    <thead>
      <tr>
        <th>
          <goa-table-sort-header name="firstName">First name</goa-table-sort-header>
        </th>
        <th>
          <goa-table-sort-header name="lastName">Last name</goa-table-sort-header>
        </th>
        <th>
          <goa-table-sort-header name="age" direction="asc">Age</goa-table-sort-header>
        </th>
      </tr>
    </thead>
    <tbody id="table-body"></tbody>
  </table>
</goa-table>

Enable column sorting in tables using sort headers, allowing workers to organize data by clicking column headers to toggle ascending/descending order.

When to use

Use this pattern when:

  • Workers need to organize tabular data by different columns
  • Data sets are large enough that sorting improves usability
  • Multiple columns could reasonably be used as sort criteria

Considerations

  • Indicate the current sort direction with visual cues
  • Set a sensible default sort order when the table loads
  • Consider which columns should be sortable based on data type
  • Numeric columns typically sort numerically, text columns alphabetically
  • Maintain sort state when data updates if appropriate