Welcome to a quick tutorial and examples on how to freeze rows and columns in HTML tables. Need to “fix a table row or column”? Only to find out that there are no direct ways to do so in HTML and CSS?
The possible ways to freeze a row/column in HTML and CSS are:
- Limit the height of the table body and set it to overflow.
thead, tbody { display: block; }
tbody { max-height: 100px; overflow: auto; }
- Set a sticky table header –
thead { position: sticky; top: 0; }
- To freeze a column:
- Set the first cell of the row as a header –
<tr> <th>HEAD</th> <td>CELL</td><td>CELL</td> </tr>
- Set the header cell to stick –
th { position: sticky; left: 0; }
- Set the first cell of the row as a header –
That covers the quick basics, but read on for detailed examples!
TABLE OF CONTENTS
DOWNLOAD & NOTES
Here is the download link to the example code, so you don’t have to copy-paste everything.
EXAMPLE CODE DOWNLOAD
Click here to download | Example on CodePen
Just click on “download zip” or do a git clone. I have released it under the MIT license, so feel free to build on top of it or use it in your own project.
SORRY FOR THE ADS...
But someone has to pay the bills, and sponsors are paying for it. I insist on not turning Code Boxx into a "paid scripts" business, and I don't "block people with Adblock". Every little bit of support helps.
Buy Me A Coffee Code Boxx eBooks
FREEZE TABLE ROWS & COLUMNS
All right, let us now get the various possible ways to freeze the table rows and columns in HTML.
TUTORIAL VIDEO
METHOD 1) FREEZE HEADER ROW WITH OVERFLOW
<style>
/* (A) DISPLAY AS BLOCK */
#demoA thead, #demoA tbody { display: block; }
/* (B) LIMIT HEIGHT & SET OVERFLOW */
#demoA tbody {
max-height: 200px;
overflow: auto;
}
/* (X) COSMETICS - NOT IMPORTANT */
#demoA { width:100%; }
#demoA th, #demoA td {
width: 100px;
font-size: 20px;
padding: 10px;
text-align: left;
}
#demoA thead { background: #ffebec; }
#demoA tbody { background: #e4ebff; }
</style>
<table id="demoA">
<thead>
<tr><th>Head</th><th>Head</th></tr>
</thead>
<tbody>
<tr><td>Cell</td><td>Cell</td></tr>
<tr><td>Cell</td><td>Cell</td></tr>
</tbody>
</table>
Head | Head |
---|---|
Cell | Cell |
Cell | Cell |
Cell | Cell |
Cell | Cell |
Cell | Cell |
This seems to be a common solution on the Internet. It is not the prettiest, but at least it is simple and it works.
- Just set the head and body sections of the table to
display: block
. - Then limit the height of the table body, set it to auto overflow
tbody { max-height: XYZpx; overflow: auto; }
METHOD 2) STICKY HEADER
/* (A) STICKY HEADER */
#demoB thead {
position: sticky;
top: 0;
z-index: 2;
}
/* (X) COSMETICS - NOT IMPORTANT */
#demoB { width:100%; }
#demoB th, #demoB td {
font-size: 20px;
padding: 10px;
text-align: left;
}
#demoB th { background: #ffebec; }
#demoB td { background: #e4ebff; }
</style>
<table id="demoB">
<thead>
<tr><th>Head</th><th>Head</th></tr>
</thead>
<tbody>
<tr><td>Cell</td><td>Cell</td></tr>
<tr><td>Cell</td><td>Cell</td></tr>
</tbody>
</table>
Head | Head |
---|---|
Cell | Cell |
Cell | Cell |
Cell | Cell |
Cell | Cell |
Cell | Cell |
This is an alternative to the previous method, where we set the table header to position: sticky
. I think this is more elegant – Modern browsers “automatically understand” this, and will make the header follow as you scroll down.
METHOD 3) FREEZE COLUMN
<style>
/* (A) TABLE WRAPPER */
#demoCW { overflow: auto; }
/* (B) STICKY HEADERS */
#demoCT th {
position: sticky;
left: 0;
z-index: 2;
}
/* (X) COSMETICS - NOT IMPORTANT */
#demoCT th, #demoCT td {
font-size: 20px;
padding: 10px;
text-align: left;
min-width: 300px;
}
#demoCT th { background: #ffebec; }
#demoCT td { background: #e4ebff; }
</style>
<div id="demoCW"><table id="demoCT">
<tr>
<th>Head</th>
<td>Cell</td><td>Cell</td><td>Cell</td>
</tr>
<tr>
<th>Head</th>
<td>Cell</td><td>Cell</td><td>Cell</td>
</tr>
</table></div>
Head | Cell | Cell | Cell |
---|---|---|---|
Head | Cell | Cell | Cell |
Head | Cell | Cell | Cell |
Head | Cell | Cell | Cell |
Head | Cell | Cell | Cell |
Slightly more roundabout to freeze columns.
- Create a wrapper with
overflow: auto
around the table. - Set the first cell of each row to
<th>
. - Lastly, set the header cells to
position: sticky
, but toleft: 0
this time.
METHOD 4) FREEZE ROW & COLUMN
<style>
/* (A) TABLE WRAPPER */
#demoDW {
max-height: 200px;
overflow: auto;
}
/* (B) STICKY HEADERS */
#demoDT thead, #demoDT th { position: sticky; }
#demoDT thead { top: 0; z-index: 2; }
#demoDT th { left: 0; z-index: 1; }
/* (X) COSMETICS - NOT IMPORTANT */
#demoDT th, #demoDT td {
font-size: 20px;
padding: 10px;
text-align: left;
min-width: 300px;
}
#demoDT thead th { background: #b7ffd5; }
#demoDT th { background: #ffebec; }
#demoDT td { background: #e4ebff; }
</style>
<div id="demoDW"><table id="demoDT">
<thead>
<tr>
<th>Head</th><th>Head</th>
<th>Head</th><th>Head</th>
</tr>
</thead>
<tbody>
<tr>
<th>Head</th>
<td>Cell</td><td>Cell</td><td>Cell</td>
</tr>
<tr>
<th>Head</th>
<td>Cell</td><td>Cell</td><td>Cell</td>
</tr>
</tbody>
</table></div>
Head | Head | Head | Head |
---|---|---|---|
Head | Cell | Cell | Cell |
Head | Cell | Cell | Cell |
Head | Cell | Cell | Cell |
Head | Cell | Cell | Cell |
Head | Cell | Cell | Cell |
To freeze both the header row and first column, this is pretty much a combination of the previous examples.
- Create a wrapper with
overflow: auto
around the table. - Same old tactic – Set the first header row in
<thead>
, and the first cell of each row to<th>
. - Set sticky on both
<thead>
and<th>
. - But take note of the order here,
<thead>
is on top of<th>
. If you want the other way, swap thez-index
accordingly.
EXTRAS
That’s all for the tutorial, and here is a small section on some extras and links that may be useful to you.
INFOGRAPHIC CHEATSHEET
LINKS & REFERENCES
- Ways To Create Responsive Tables – Code Boxx
- Fixed Table Headers – Adrain Roselli
- Freezing Row and Column in HTML Table Using CSS – Perficient
THE END
Thank you for reading, and we have come to the end. I hope that it has helped you to better understand, and if you want to share anything with this guide, please feel free to comment below. Good luck and happy coding!
For method 4) freeze row & column, the horizontal scroll of the headers needs an additional CSS statement:
#demoDT thead th:first-child { z-index: 3; }
in order for the header of the left column to stay above the headers of the other columns
how about 2 sticky column?
Not tested, but you can try using
colgroup
and sticky the group instead…Sweet little solution