Turns out, creating an HTML table that has both horizontal scroll and fixed headers can be a tricky problem. As another developer said about this problem, You would think this is easy. But is really isn't.

Horizontal Scroll

When working with a table that has a lot of columns, you can ensure everything remains visible by adding horizontal scroll. You can do this by wrapping the table in a div that has overflow-x set to auto.

Note: I prefer to use overflow-x: auto instead of overflow-x: scroll because scroll will always show the scroll container, even if there is no scroll available. It looks cleaner.

style.css
.table-outer {
  overflow-x: auto;
}
index.html
<div class="table-outer">
  <table>
    <thead>
      /* table columns go here */
    </thead>
    <tbody>
      /* table data goes here... */
    </tbody>
  </table>
</div>

This works great initially, but there can be some problems.

  • If you have a lot of rows, as you scroll down, you can't see which table headers are associated with each cell
  • If your table rows are selectable and you have bulk actions in the header, you won't see them as you scroll down
  • If there is more content on the page below the table, you won't see the headers as you scroll down

Sticky Headers

You might think that adding position: sticky to the thead of the table would ensure that as you scroll down, the headers will remain fixed to the top of the screen until you scroll past the table.

style.css
.table-outer {
  overflow-x: auto;
}

thead {
  position: sticky;
  z-index: 2;
  top: 0;
}

But as you can see in this example, the table headers are not fixed to the top of the screen as you scroll down.

Since you're now using an overflow container, position: sticky will only apply to the overflow container, and if you're not scrolling within it, it will just be ignored.

Solution

The solution was to add a height to the table wrapper. In this example, I chose to make the height 100% of the viewport window, minus 100px to enable the page title and footer to always be visible. This results in a full-screen datagrid type view.

style.css
.table-outer {
  overflow-x: auto;
  height: calc(100vh - 100px); /* full height minus header and footer */
}

header {
  height: 60px;
}

footer {
  height: 40px;
}

As you can see here, the page title and footer are always visible, and the table is both horizontally and vertically scrollable. This table has fixed headers and also the first row (ID) is fixed to the left of the screen as you scroll horizontally.

Conclusion

And that's all! Hopefully this helps anyone that is struggling to figure out a good solution for their scrollable table.