<div class="container">
  <div class="row">
    <div class="col-12">
      <h1>Display Flex & HTML5 Table with sticky headers</h1>
    </div>
    <div class="col-6">
      <h2>HTML Table</h2>
      <p>This is a native HTML Table that is scrollable with no sticky headers.</p>
      <div class="table-scroll table-native">
        <table class="table table-hover">
          <thead>
            <tr>
              <th>one</th>
              <th>two</th>
              <th>three</th>
              <th>four</th>
              <th>five</th>
            </tr>
          </thead>
          <tbody class="scroll">
            <tr>
              <td>r1 content 1</td>
              <td>r1 content 2</td>
              <td>r1 content 3</td>
              <td>r1 content 4</td>
              <td>r1 content 5</td>
            </tr>
            <tr>
              <td>r2 content 1</td>
              <td>r2 content 2</td>
              <td>r2 content 3</td>
              <td>r2 content 4</td>
              <td>r2 content 5</td>
            </tr>
            <tr>
              <td>r3 content 1</td>
              <td>r3 content 2</td>
              <td>r3 content 3</td>
              <td>r3 content 4</td>
              <td>r3 content 5</td>
            </tr>
            <tr>
              <td>r4 content 1</td>
              <td>r4 content 2</td>
              <td>r4 content 3</td>
              <td>r4 content 4</td>
              <td>r4 content 5</td>
            </tr>
            <tr>
              <td>r5 content 1</td>
              <td>r5 content 2</td>
              <td>r5 content 3</td>
              <td>r5 content 4</td>
              <td>r5 content 5</td>
            </tr>
            <tr>
              <td>r6 content 1</td>
              <td>r6 content 2</td>
              <td>r6 content 3</td>
              <td>r6 content 4</td>
              <td>r6 content 5</td>
            </tr>
            <tr>
              <td>r7 content 1</td>
              <td>r7 content 2</td>
              <td>r7 content 3</td>
              <td>r7 content 4</td>
              <td>r7 content 5</td>
            </tr>
            <tr>
              <td>r8 content 1</td>
              <td>r8 content 2</td>
              <td>r8 content 3</td>
              <td>r8 content 4</td>
              <td>r8 content 5</td>
            </tr>
            <tr>
              <td>r9 content 1</td>
              <td>r9 content 2</td>
              <td>r9 content 3</td>
              <td>r9 content 4</td>
              <td>r9 content 5</td>
            </tr>
            <tr>
              <td>r10 content 1</td>
              <td>r10 content 2</td>
              <td>r10 content 3</td>
              <td>r10 content 4</td>
              <td>r10 content 5</td>
            </tr>
            <tr>
              <td>r11 content 1</td>
              <td>r11 content 2</td>
              <td>r11 content 3</td>
              <td>r11 content 4</td>
              <td>r11 content 5</td>
            </tr>
            <tr>
              <td>r12 content 1</td>
              <td>r12 content 2</td>
              <td>r12 content 3</td>
              <td>r12 content 4</td>
              <td>r12 content 5</td>
            </tr>
          </tbody>
        </table>
      </div>
    </div>
    <div class="col-6">
      <h2>Flex Table with Sticky Headers</h2>
      <p>This is a native HTML table utilizing CSS <code>flex</code> properties for sticky headers.</p>
      <div class="table-scroll">
        <table class="flex-table flex-table-aligned table table-striped table-hover">
            <thead>
              <tr>
                <th>one</th>
                <th>two</th>
                <th>three</th>
                <th>four</th>
                <th>five</th>
              </tr>
            </thead>
            <tbody class="scroll">
              <tr>
                <td>r1 content 1</td>
                <td>r1 content 2</td>
                <td>r1 content 3</td>
                <td>r1 content 4</td>
                <td>r1 content 5</td>
              </tr>
              <tr>
                <td>r2 content 1</td>
                <td>r2 content 2</td>
                <td>r2 content 3</td>
                <td>r2 content 4</td>
                <td>r2 content 5</td>
              </tr>
              <tr>
                <td>r3 content 1</td>
                <td>r3 content 2</td>
                <td>r3 content 3</td>
                <td>r3 content 4</td>
                <td>r3 content 5</td>
              </tr>
              <tr>
                <td>r4 content 1</td>
                <td>r4 content 2</td>
                <td>r4 content 3</td>
                <td>r4 content 4</td>
                <td>r4 content 5</td>
              </tr>
              <tr>
                <td>r5 content 1</td>
                <td>r5 content 2</td>
                <td>r5 content 3</td>
                <td>r5 content 4</td>
                <td>r5 content 5</td>
              </tr>
              <tr>
                <td>r6 content 1</td>
                <td>r6 content 2</td>
                <td>r6 content 3</td>
                <td>r6 content 4</td>
                <td>r6 content 5</td>
              </tr>
              <tr>
                <td>r7 content 1</td>
                <td>r7 content 2</td>
                <td>r7 content 3</td>
                <td>r7 content 4</td>
                <td>r7 content 5</td>
              </tr>
              <tr>
                <td>r8 content 1</td>
                <td>r8 content 2</td>
                <td>r8 content 3</td>
                <td>r8 content 4</td>
                <td>r8 content 5</td>
              </tr>
              <tr>
                <td>r9 content 1</td>
                <td>r9 content 2</td>
                <td>r9 content 3</td>
                <td>r9 content 4</td>
                <td>r9 content 5</td>
              </tr>
              <tr>
                <td>r10 content 1</td>
                <td>r10 content 2</td>
                <td>r10 content 3</td>
                <td>r10 content 4</td>
                <td>r10 content 5</td>
              </tr>
              <tr>
                <td>r11 content 1</td>
                <td>r11 content 2</td>
                <td>r11 content 3</td>
                <td>r11 content 4</td>
                <td>r11 content 5</td>
              </tr>
              <tr>
                <td>r12 content 1</td>
                <td>r12 content 2</td>
                <td>r12 content 3</td>
                <td>r12 content 4</td>
                <td>r12 content 5</td>
              </tr>
            </tbody>
          </table>
      </div>
    </div>

  </div>
  <div class="row">
    <div class="col-12">
      <h2>Setup</h2>
    </div>
    <div class="col-12">
      <p>Add display flex.</p>
      <ul>
        <li>Add display flex with flex-direction column to the <code>&lt;table&gt;</code> & set height to 100% (will be 100% in relation to parent container)</li>
        <li>Add display flex to <code>&lt;tr&gt;</code></li>
        <li>Add display block to <code>&lt;thead&gt;</code> & <code>&lt;tbody&gt;</code> (support for internet explorer 9+ so columns align correctly)</li>
        <li>Add overflow-y auto or scroll to <code>&lt;tbody&gt;</code> (so the table body only scrolls)</li>
        <li>Add flex 1 to <code>&lt;tbody&gt;</code> (sets the table body to flex accordingly to height of table) ***we do not add a height or flex to the table header, ie <code>&lt;thead&gt;</code>, so the header elements will wrap correctly</li>
      </ul>
    </div>
    <div class="col-6">
      <h3>SCSS properties</h3>
      <pre>
table.flex-table {
  display: flex;
  flex-direction: column;
  height: 100%;
  thead, tbody {
    display: block;
  }
  tbody {
    flex: 1;
    overflow-y: scroll;
  }
  tr {
    display: flex;
    // flex-direction by default is row (optional)
    flex-direction: row;
    td, th {
      // add display block for internet explorer 9+
      display: block;
      flex: 1;
      // control column widths with :nth-child(n)
      &:nth-child(n) {
        flex: n;
      }
    }
  }
}
</pre>
    </div>
    <div class="col-6">
      <h3>Column Width</h3>
      <p>Using <code>display: flex</code>, all columns will have a flex width of 1 by default. To dictate width of columns, use :nth-child(n) on tr>th, tr>td.</p>
      <pre>
tr {
  th,td {
    // changing column 2 width
    &:nth-child(2) {
      flex: 3;
    }
    // changing column 4 width
    &:nth-child(4) {
      flex: 2;
    }
  }
}
</pre>
    </div>
    <div class="col-12">
      <h3>Table with custom flex widths</h3>
      <p>Example where column 2 makes use of <code>flex: 3</code> and column 4 makes use of <code>flex: 2</code> for custom column widths.</p>
      <div class="table-scroll mb-3">
        <table class="flex-table flex-widths table table-hover table-bordered">
          <thead>
            <tr>
              <th>one</th>
              <th>two <span class="badge bg-info text-dark">&nbsp;( flex width 3 )&nbsp;</span></th>
              <th>three</th>
              <th>four <span class="badge bg-info text-dark">&nbsp;( flex width 2 )&nbsp;</span></th>
              <th>five</th>
            </tr>
          </thead>
          <tbody class="scroll">
            <tr>
              <td>r1 content 1</td>
              <td>r1 content 2</td>
              <td>r1 content 3</td>
              <td>r1 content 4</td>
              <td>r1 content 5</td>
            </tr>
            <tr>
              <td>r2 content 1</td>
              <td>r2 content 2</td>
              <td>r2 content 3</td>
              <td>r2 content 4</td>
              <td>r2 content 5</td>
            </tr>
            <tr>
              <td>r3 content 1</td>
              <td>r3 content 2</td>
              <td>r3 content 3</td>
              <td>r3 content 4</td>
              <td>r3 content 5</td>
            </tr>
            <tr>
              <td>r4 content 1</td>
              <td>r4 content 2</td>
              <td>r4 content 3</td>
              <td>r4 content 4</td>
              <td>r4 content 5</td>
            </tr>
            <tr>
              <td>r5 content 1</td>
              <td>r5 content 2</td>
              <td>r5 content 3</td>
              <td>r5 content 4</td>
              <td>r5 content 5</td>
            </tr>
            <tr>
              <td>r6 content 1</td>
              <td>r6 content 2</td>
              <td>r6 content 3</td>
              <td>r6 content 4</td>
              <td>r6 content 5</td>
            </tr>
            <tr>
              <td>r7 content 1</td>
              <td>r7 content 2</td>
              <td>r7 content 3</td>
              <td>r7 content 4</td>
              <td>r7 content 5</td>
            </tr>
            <tr>
              <td>r8 content 1</td>
              <td>r8 content 2</td>
              <td>r8 content 3</td>
              <td>r8 content 4</td>
              <td>r8 content 5</td>
            </tr>
            <tr>
              <td>r9 content 1</td>
              <td>r9 content 2</td>
              <td>r9 content 3</td>
              <td>r9 content 4</td>
              <td>r9 content 5</td>
            </tr>
            <tr>
              <td>r10 content 1</td>
              <td>r10 content 2</td>
              <td>r10 content 3</td>
              <td>r10 content 4</td>
              <td>r10 content 5</td>
            </tr>
            <tr>
              <td>r11 content 1</td>
              <td>r11 content 2</td>
              <td>r11 content 3</td>
              <td>r11 content 4</td>
              <td>r11 content 5</td>
            </tr>
            <tr>
              <td>r12 content 1</td>
              <td>r12 content 2</td>
              <td>r12 content 3</td>
              <td>r12 content 4</td>
              <td>r12 content 5</td>
            </tr>
          </tbody>
        </table>
      </div>
    </div>
    <div class="col-12">
      <div class="row">
        <div class="col-6">
          <h3>Header alignment or lack thereof</h3>
          <p>When the <code>&lt;tbody&gt;</code> has the overflow-y set to scroll, there will be a slight misalignment with the table headers and table body. To account for this misalignment, we can add an <code>overflow-y: scroll;</code> and <code>overflow-x: hidden</code> to our CSS <code>thead tr</code> selector.</p>
        </div>

        <div class="col-6">
          <pre>
table {
  thead > tr {
    overflow-y: scroll;
    overflow-x: hidden;
  }
}
</pre>
        </div>
      </div>
    </div>
    <div class="col-12">
      <div class="table-scroll mb-3">
        <table class="flex-table table table-striped table-hover table-bordered flex-widths flex-table-aligned">
            <thead>
              <tr>
                <th>one</th>
                <th>two <span class="badge bg-info text-dark">&nbsp;( flex width 3 )&nbsp;</span></th>
                <th>three</th>
                <th>four <span class="badge bg-info text-dark">&nbsp;( flex width 2 )&nbsp;</span></th>
                <th>five</th>
              </tr>
            </thead>
            <tbody class="scroll">
              <tr>
                <td>r1 content 1</td>
                <td>r1 content 2</td>
                <td>r1 content 3</td>
                <td>r1 content 4</td>
                <td>r1 content 5</td>
              </tr>
              <tr>
                <td>r2 content 1</td>
                <td>r2 content 2</td>
                <td>r2 content 3</td>
                <td>r2 content 4</td>
                <td>r2 content 5</td>
              </tr>
              <tr>
                <td>r3 content 1</td>
                <td>r3 content 2</td>
                <td>r3 content 3</td>
                <td>r3 content 4</td>
                <td>r3 content 5</td>
              </tr>
              <tr>
                <td>r4 content 1</td>
                <td>r4 content 2</td>
                <td>r4 content 3</td>
                <td>r4 content 4</td>
                <td>r4 content 5</td>
              </tr>
              <tr>
                <td>r5 content 1</td>
                <td>r5 content 2</td>
                <td>r5 content 3</td>
                <td>r5 content 4</td>
                <td>r5 content 5</td>
              </tr>
              <tr>
                <td>r6 content 1</td>
                <td>r6 content 2</td>
                <td>r6 content 3</td>
                <td>r6 content 4</td>
                <td>r6 content 5</td>
              </tr>
              <tr>
                <td>r7 content 1</td>
                <td>r7 content 2</td>
                <td>r7 content 3</td>
                <td>r7 content 4</td>
                <td>r7 content 5</td>
              </tr>
              <tr>
                <td>r8 content 1</td>
                <td>r8 content 2</td>
                <td>r8 content 3</td>
                <td>r8 content 4</td>
                <td>r8 content 5</td>
              </tr>
              <tr>
                <td>r9 content 1</td>
                <td>r9 content 2</td>
                <td>r9 content 3</td>
                <td>r9 content 4</td>
                <td>r9 content 5</td>
              </tr>
              <tr>
                <td>r10 content 1</td>
                <td>r10 content 2</td>
                <td>r10 content 3</td>
                <td>r10 content 4</td>
                <td>r10 content 5</td>
              </tr>
              <tr>
                <td>r11 content 1</td>
                <td>r11 content 2</td>
                <td>r11 content 3</td>
                <td>r11 content 4</td>
                <td>r11 content 5</td>
              </tr>
              <tr>
                <td>r12 content 1</td>
                <td>r12 content 2</td>
                <td>r12 content 3</td>
                <td>r12 content 4</td>
                <td>r12 content 5</td>
              </tr>
            </tbody>
          </table>
      </div>
    </div>
    <div class="col-12">
      <h2>Additional resources</h2>
      <p>For more info on CSS3 display flex, you can visit <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/flex">MDN</a>. You can also visit <a href="http://the-echoplex.net/flexyboxes/">echoplex</a> to see an interactive demo of flexbox in action.</p>
    </div>
  </div>

</div>
// table stylings
.table-scroll {
  height: 300px;
  padding: 0px;
  &.table-native {
    overflow-y: auto;
  }
  table {
    &.flex-table {
      display: flex;
      flex-direction: column;
      height: 100%;
      thead,
      tbody {
        display: block;
      }
      thead {
        margin-right: 0px; //margin to align correctly to scrollbar in table body
      }
      tbody {
        flex: 1; //variable height
        overflow-y: scroll;
      }
      tr {
        width: 100%;
        display: flex;
        td,
        th {
          display: block;
          flex: 1;
        }
      }
    }
    &.flex-widths {
      tr {
        td,
        th {
          &:nth-child(2) {
            flex: 3;
          }
          &:nth-child(4) {
            flex: 2;
          }
        }
      }
    }
    &.flex-table-aligned {
      thead > tr {
        overflow-y: scroll;
        overflow-x: hidden;
      }
    }
  }
}
// table stylings end

// markup styling
pre {
  background: #6c757d;
  padding: 10px;
  color: white;
}
code {
  background: #6c757d;
  padding: 3px 6px;
  color: white;
}
View Compiled

External CSS

  1. https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.0.2/css/bootstrap.min.css

External JavaScript

  1. https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js
  2. https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.1.3/js/bootstrap.min.js