How to Create a Table with a Fixed Header and Scrollable Body

Solutions with CSS properties

In this tutorial, find some methods of creating an HTML table, which has a fixed header and scrollable body. Of course, you need to use CSS.

It is possible to achieve such a result by setting the position property to “sticky” and specifying 0 as a value of the top property for the <th> element.

As usual, you can open the Try it Yourself demo link and play with the properties to understand them better.

You can also read the comments in front of each line to understand the properties better.

Example of creating a table with a scrollable body by using the position property:

<!DOCTYPE html>
<html>
  <head>
    <title>Title of the document</title>
    <style>
      .tableFixHead {
        overflow-y: auto; /* make the table scrollable if height is more than 200 px  */
        height: 200px; /* gives an initial height of 200px to the table */
      }
      .tableFixHead thead th {
        position: sticky; /* make the table heads sticky */
        top: 0px; /* table head will be placed from the top of the table and sticks to it */
      }
      table {
        border-collapse: collapse; /* make the table borders collapse to each other */
        width: 100%;
      }
      th,
      td {
        padding: 8px 16px;
        border: 1px solid #ccc;
      }
      th {
        background: #eee;
      }
    </style>
  </head>
  <body>
    <div class="tableFixHead">
      <table>
        <thead>
          <tr>
            <th>Col 1</th>
            <th>Col 2</th>
          </tr>
        </thead>
        <tbody>
          <tr>
            <td>1.1</td>
            <td>2.1</td>
          </tr>
          <tr>
            <td>1.2</td>
            <td>2.2</td>
          </tr>
          <tr>
            <td>1.3</td>
            <td>2.3</td>
          </tr>
          <tr>
            <td>1.4</td>
            <td>2.4</td>
          </tr>
          <tr>
            <td>1.5</td>
            <td>2.5</td>
          </tr>
          <tr>
            <td>1.6</td>
            <td>2.5</td>
          </tr>
          <tr>
            <td>1.7</td>
            <td>2.5</td>
          </tr>
          <tr>
            <td>1.8</td>
            <td>2.5</td>
          </tr>
        </tbody>
      </table>
    </div>
  </body>
</html>

Result

Col 1 Col 2
1.1 2.1
1.2 2.2
1.3 2.3
1.4 2.4
1.5 2.5
1.6 2.5
1.7 2.5
1.8 2.5

Great! Huh? But let's face it! I don't like to see that scrollbar starting from the head section of the table!

So, let's continue to the next example and fix that issue together!

Thers's another method of creating a table with a fixed header and scrollable body. In the example below, we set the display to “block” for the <tbody> element so that it’s possible to apply the height and overflow properties.

Example of creating a table with a scrollable body by using the display property:

<!DOCTYPE html>
<html>
  <head>
    <title>Title of the document</title>
    <style>
      .fixed_header {
        width: 400px;
        table-layout: fixed;
        border-collapse: collapse;
      }
      .fixed_header tbody {
        display: block;
        width: 100%;
        overflow: auto;
        height: 100px;
      }
      .fixed_header thead tr {
        display: block;
      }
      .fixed_header thead {
        background: black;
        color: #fff;
      }
      .fixed_header th,
      .fixed_header td {
        padding: 5px;
        text-align: left;
        width: 200px;
      }
    </style>
  </head>
  <body>
    <table class="fixed_header">
      <thead>
        <tr>
          <th>Col 1</th>
          <th>Col 2</th>
          <th>Col 3</th>
          <th>Col 4</th>
          <th>Col 5</th>
        </tr>
      </thead>
      <tbody>
        <tr>
          <td>1</td>
          <td>2</td>
          <td>3</td>
          <td>4</td>
          <td>5</td>
        </tr>
        <tr>
          <td>6</td>
          <td>7</td>
          <td>8</td>
          <td>9</td>
          <td>10</td>
        </tr>
        <tr>
          <td>11</td>
          <td>12</td>
          <td>13</td>
          <td>14</td>
          <td>15</td>
        </tr>
        <tr>
          <td>16</td>
          <td>17</td>
          <td>18</td>
          <td>19</td>
          <td>20</td>
        </tr>
        <tr>
          <td>21</td>
          <td>22</td>
          <td>23</td>
          <td>24</td>
          <td>25</td>
        </tr>
      </tbody>
    </table>
  </body>
</html>

As mentioned before, we used overflow: auto on the tbody along with the display: block.

Here's our result, and enjoy the difference!

Col 1 Col 2 Col 3 Col 4 Col 5
1 2 3 4 5
6 7 8 9 10
11 12 13 14 15
16 17 18 19 20
21 22 23 24 25

As you have noticed, we didn’t use borders in the previous examples. However, if you need to add borders, you can simply use border property on all td tags.

.fixed_header td {
  border: 1px solid #797878;
}

And here's the result.

Col 1 Col 2 Col 3 Col 4 Col 5
1 2 3 4 5
6 7 8 9 10
11 12 13 14 15
16 17 18 19 20
21 22 23 24 25

Hope you've enjoyed it!