Muhi Logo Text
AboutBlogWork With Me

How to Create a Sticky Header & Column in React

Learn how to create a scrollable table with a sticky header and column in React.

Last updated on April 22, 2024

react
html
css
Sticky Header & Column in React

In the modern digital era, where data is growing faster than ever, efficiently handling and displaying complex tabular data becomes crucial for the UI. Whether it’s financial reports, inventory management, or extensive datasets for machine learning, the ability to scroll through large amounts of data without losing context is essential. This is where the concept of sticky headers and columns comes into play, providing a fixed point of reference that enhances readability and user experience.

In this tutorial, we will build a React component that features a table with sticky headers and columns. This means that the header row and specific columns will remain visible on the screen even as we scroll vertically or horizontally through our dataset.

Creating a Scrollable Table in React

To start, we’ll build a basic scrollable table in React that displays student information. This table will dynamically populate rows based on a dataset and be styled to support both vertical and horizontal scrolling. Let’s break down the steps and write the necessary code.

Step 1: Creating a Basic Table Component

import "./App.css";

const students = [
 {
  id: 1,
  name: "John Doe",
  age: 20,
  course: "Business",
  grade: "A",
  attendance: "Present",
  status: "Active",
  remarks: "Good",
 },
 {
  id: 2,
  name: "Jane Smith",
  age: 22,
  course: "Engineering",
  grade: "B",
  attendance: "Absent",
  status: "Inactive",
  remarks: "Average",
 },
 {
  id: 3,
  name: "Alice Johnson",
  age: 21,
  course: "Medicine",
  grade: "A+",
  attendance: "Present",
  status: "Active",
  remarks: "Excellent",
 },
 {
  id: 4,
  name: "Bob Brown",
  age: 23,
  course: "Computer Science",
  grade: "C",
  attendance: "Present",
  status: "Active",
  remarks: "Fair",
 },
 {
  id: 5,
  name: "Eve Wilson",
  age: 24,
  course: "Arts",
  grade: "B+",
  attendance: "Absent",
  status: "Inactive",
  remarks: "Good",
 },
 {
  id: 6,
  name: "David Lee",
  age: 25,
  course: "Law",
  grade: "A-",
  attendance: "Present",
  status: "Active",
  remarks: "Very Good",
 },
 {
  id: 7,
  name: "Grace Clark",
  age: 26,
  course: "Science",
  grade: "D",
  attendance: "Absent",
  status: "Inactive",
  remarks: "Poor",
 },
];

const TableComponent = () => {
 return (
  <table>
   <thead>
    <tr>
     <th>ID</th>
     <th>Name</th>
     <th>Age</th>
     <th>Course</th>
     <th>Grade</th>
     <th>Attendance</th>
     <th>Status</th>
     <th>Remarks</th>
    </tr>
   </thead>
   <tbody>
    {students.map(student => (
     <tr key={student.id}>
      <td>{student.id}</td>
      <td>{student.name}</td>
      <td>{student.age}</td>
      <td>{student.course}</td>
      <td>{student.grade}</td>
      <td>{student.attendance}</td>
      <td>{student.status}</td>
      <td>{student.remarks}</td>
     </tr>
    ))}
   </tbody>
  </table>
 );
};

export default TableComponent;

In this code:

  • We define a simple array of objects containing student data.
  • TableComponent outputs a table where we use .map() to create a row for each student.

Let’s add some styling to make the table visually appealing:

table {
 width: 100%;
 border-collapse: collapse;
}

th,
td {
 padding: 8px;
 text-align: left;
 border-bottom: 1px solid #ddd;
}

thead th {
 background-color: #f2f2f2;
}

tr:nth-child(even) {
 background-color: #f2f2f2;
}

Step 2: Enabling Scrolling

Since the <table> element itself does not support scrolling, we need to wrap the table in a div container that handles overflow.

<div className="table-container">
 <table>{/* Table content */}</table>
</div>
.table-container {
 max-height: 200px;
 max-width: 500px;
 overflow: auto;
 border: 1px solid #ddd;
}

th,
td {
 ...
 white-space: nowrap;
}

In this code:

  • We wrap TableComponent inside a <div> with a style that sets a maximum height and width and enables scrolling (overflow: auto), allowing the table to scroll vertically and horizontally when the content exceeds the specified dimensions.
  • We set white-space: nowrap on th and td elements to prevent text wrapping on smaller screens.
  • We are adding a border around the scrolling div instead of the table to make the scrollbar appear as part of the table.

Here is the basic scrollable table in action:

Implementing a Sticky Header

With the basic scrollable table set up, we can work on the sticky headers by keeping them visible at the top of a table even when the user scrolls down through a long list.

Using CSS position: sticky

The CSS position: sticky is a powerful feature that toggles between relative and fixed positioning based on the scroll position. It sticks to the top of the scrollable area when content scrolls under it, and it is supported in most modern browsers.

To make the table header sticky, we need to add specific styles to the <thead> element’s cells.

thead th {
 background-color: #f2f2f2;
 position: sticky;
 top: 0;
 z-index: 1;
}

In this code:

  • We set the position of the <th> elements inside the <thead> to sticky.
  • The top: 0 property ensures that the header sticks to the top of the scrollable area.
  • The z-index: 1 property ensures the header remains above the table content.

Implementing a Sticky Column

As sticky headers provide a constant visual reference for the data rows, sticky columns are equally beneficial when navigating through wide tables requiring horizontal scrolling.

Implementing sticky columns involves a similar approach to sticky headers but targets horizontal positioning.

Let’s modify the existing styles to include sticky columns. We’ll start with making the first column sticky:

th:first-child,
td:first-child {
 position: sticky;
 left: 0;
 z-index: 2;
 background-color: #f2f2f2;
}

th:first-child {
 z-index: 3;
}

In this code:

  • We target the first <th> and <td> elements in the table.
  • We set the position of the first column to sticky.
  • The left: 0 property ensures that the first column sticks to the left side of the scrollable area.
  • The z-index property ensures that the first column remains above the header row.

To make multiple columns sticky, we can adjust the left value of each subsequent column, accounting for the width of the preceding columns. This requires calculating or setting fixed widths for these columns:

th:first-child,
td:first-child {
 position: sticky;
 left: 0;
 z-index: 2;
 background-color: #f2f2f2;
}

th:nth-child(2),
td:nth-child(2) {
 position: sticky;
 left: 32px; /* Adjust based on the width of the first column */
 z-index: 2;
 background-color: #f2f2f2;
}

th:first-child,
th:nth-child(2) {
 z-index: 3;
}

/* Repeat for additional columns */

Below is the complete code with a live demo:

import "./App.css";

const students = [
 {
  id: 1,
  name: "John Doe",
  age: 20,
  course: "Business",
  grade: "A",
  attendance: "Present",
  status: "Active",
  remarks: "Good",
 },
 {
  id: 2,
  name: "Jane Smith",
  age: 22,
  course: "Engineering",
  grade: "B",
  attendance: "Absent",
  status: "Inactive",
  remarks: "Average",
 },
 {
  id: 3,
  name: "Alice Johnson",
  age: 21,
  course: "Medicine",
  grade: "A+",
  attendance: "Present",
  status: "Active",
  remarks: "Excellent",
 },
 {
  id: 4,
  name: "Bob Brown",
  age: 23,
  course: "Computer Science",
  grade: "C",
  attendance: "Present",
  status: "Active",
  remarks: "Fair",
 },
 {
  id: 5,
  name: "Eve Wilson",
  age: 24,
  course: "Arts",
  grade: "B+",
  attendance: "Absent",
  status: "Inactive",
  remarks: "Good",
 },
 {
  id: 6,
  name: "David Lee",
  age: 25,
  course: "Law",
  grade: "A-",
  attendance: "Present",
  status: "Active",
  remarks: "Very Good",
 },
 {
  id: 7,
  name: "Grace Clark",
  age: 26,
  course: "Science",
  grade: "D",
  attendance: "Absent",
  status: "Inactive",
  remarks: "Poor",
 },
];

const TableComponent = () => {
 return (
  <div className="table-container">
   <table>
    <thead>
     <tr>
      <th>ID</th>
      <th>Name</th>
      <th>Age</th>
      <th>Course</th>
      <th>Grade</th>
      <th>Attendance</th>
      <th>Status</th>
      <th>Remarks</th>
     </tr>
    </thead>
    <tbody>
     {students.map(student => (
      <tr key={student.id}>
       <td>{student.id}</td>
       <td>{student.name}</td>
       <td>{student.age}</td>
       <td>{student.course}</td>
       <td>{student.grade}</td>
       <td>{student.attendance}</td>
       <td>{student.status}</td>
       <td>{student.remarks}</td>
      </tr>
     ))}
    </tbody>
   </table>
  </div>
 );
};

export default TableComponent;
.table-container {
 max-height: 200px;
 max-width: 500px;
 overflow: auto;
 border: 1px solid #ddd;
}

table {
 width: 100%;
 border-collapse: collapse;
 font-family: Arial, sans-serif;
}

th,
td {
 padding: 8px;
 text-align: left;
 border-bottom: 1px solid #ddd;
 white-space: nowrap;
}

thead th {
 background-color: #f2f2f2;
 position: sticky;
 top: 0;
 z-index: 1;
}

tr:nth-child(even) {
 background-color: #f2f2f2;
}

th:first-child,
td:first-child {
 position: sticky;
 left: 0;
 z-index: 2;
 background-color: #f2f2f2;
}

th:nth-child(2),
td:nth-child(2) {
 position: sticky;
 left: 32px; /* Adjust based on the width of the first column */
 z-index: 2;
 background-color: #f2f2f2;
}

th:first-child,
th:nth-child(2) {
 z-index: 3;
}

/* Repeat for additional columns */

Conclusion

In this tutorial, we learned how to create a scrollable table with sticky headers and columns in React. By combining CSS position: sticky with specific z-index values, we achieved a fixed header and column effect that enhances the user experience when navigating through large datasets.

Bye for now, and happy coding!

If you enjoyed this post, I regularly share similar content on Twitter. Follow me @muhimasri to stay up to date, or feel free to message me on Twitter if you have any questions or comments. I'm always open to discussing the topics I write about!

Recommended Reading

Learn how to create encapsulated and reusable Fieldset component with Material UI (MUI) and React.

react
mui

Discussion

Upskill Your Frontend Development Techniques 🌟

Subscribe to stay up-to-date and receive quality front-end development tutorials straight to your inbox!

​

No spam, sales, or ads. Unsubscribe anytime you wish.

© 2024, Muhi Masri