Use CSS Grid to Make a Fixed Sidebar with Scrollable Main Body
Tags: css, react, javascript
Published: 2022-05-16 13:25:00

---
title: Use CSS Grid to Make a Fixed Sidebar with Scrollable Main Body
published: true
date: 2022-05-14 00:00:00 UTC
tags: css,react,javascript
canonical_url: https://www.paigeniedringhaus.com/blog/use-css-grid-to-make-a-fixed-sidebar-with-scrollable-main-body
---

[![Hardware Handler product list page](https://www.paigeniedringhaus.com/static/c77484bdff73005204485fddbeee0114/1e043/hardware-handler-hero.png "Hardware Handler product list page")](/static/c77484bdff73005204485fddbeee0114/679fb/hardware-handler-hero.png)

## Introduction

Last year I authored a web development course on [modernizing enterprise React applications](https://www.newline.co/courses/newline-guide-to-modernizing-an-enterprise-react-app). It was a big undertaking, but I'm proud to have put out a comprehensive guide to show developers just what it takes to build and maintain a large scale, long-running React application: tooling, testing, refactoring, design systems, and more.

For this course, I built a React project similar to what you might find when joining an existing enterprise company: it's an internal application used by product managers to manage assortments of tools and other home improvement items and select which ones should be shipped to stores to be sold. I named it "Hardware Handler".

The project started out using an outdated version of React, having no tooling for easier setup, no automated tests, no React Hooks, no state management, no component library, and so on and so forth. As the course progressed through each module, I focused one aspect of the project and showed how to update the project to today's high quality software standards: adding React Testing Library and Jest for unit tests or refactoring class-based React components to use Hooks, for example.

One interesting thing I encountered while building the sample project is that I wanted a product page that showed all the possible products available, but also have a fixed sidebar with filtering options, so users could filter down products by a particular department or a particular brand. Now you might expect that I would reach for some CSS like `position: fixed` or `position: absolute` to keep this sidebar in place and essentially remove the sidebar from the [normal flow of the DOM](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Flow_Layout/In_Flow_and_Out_of_Flow#taking_an_item_out_of_flow), but I didn't need to. Instead, all I needed were a few simple **CSS Grid** properties.

> If you're not familiar with CSS Grid, it's a relatively new addition to CSS, and it excels at dividing a page into major regions and enabling an author to align elements into columns and rows.
> 
> I'd highly recommend checking out the **[MDN docs](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Grid_Layout)** and the **[CSS-Tricks "A Complete Guide to Grid"](https://css-tricks.com/snippets/css/complete-guide-grid/)**, for more CSS Grid info.

**Today, I'll show you how to build your own fixed sidebar in an application while still allowing the main body content to scroll freely with the help of CSS Grid.**

Below is a video of what the final page looks like: notice the page title and filters on the lefthand side stay in place while the product cards scroll down in the main content space.

{% youtube mXR8hBspl90 %} 

--- 

## Set up the `ProductList` component's HTML (or JSX)

Before we can start applying the CSS Grid Layout to our page, we must actually _code_ a page full of HTML elements to apply the grid _to_.

My project was built with the React framework, so the example code I'll be walking through would normally contain React Hooks, state, functions, and JSX, but for this blog post, everything besides the JSX is largely unnecessary, so I've cut it out.

> It should be relatively straightforward to translate the remaining JSX into HTML or another JS-powered framework, if you so desire.

Here's a condensed version of the actual `ProductList` component, please note I've eliminated much of the React-specific code and things like error handling messages, loading components, etc. to focus on the HTML that our CSS will apply to.

**`ProductList.js`**

```javascript
// other imports for React, components, constants, etc.
import './ProductList.css';

const ProductList = () => {
  // all sorts of state variables here: products, loading, errors, etc.

  // logic to facilitate adding items to checkout

  // logic for filtering products based on brand name or department 

  return (
    <div className="product-list-container">
      <section className="filter-wrapper">
        <p className="filter-title">Filter by Department</p>
        <div className="filter-data">
          {filtersByDepartment.map((filter) => (
            <span key={filter.id} className="filter-item">
              <label htmlFor={filter.id}>{filter.name}</label>
              <input
                className="filter-checkbox"
                id={filter.id}
                type="checkbox"
                checked={activeFilter.includes(filter.id)}
                onChange={() => onFilterChange(filter.id)}
              />
            </span>
          ))}
        </div>
        <p className="filter-title">Filter by Brand</p>
        <div className="filter-data">
          {filtersByBrand.map((filter) => (
            <span key={filter.value} className="filter-item">
              <label htmlFor={filter.value}>{filter.name}</label>
              <input
                className="filter-checkbox"
                id={filter.value}
                type="checkbox"
                checked={activeFilter.includes(filter.value)}
                onChange={() => onFilterChange(filter.value)}
              />
            </span>
          ))}
        </div>
      </section>
      <h1 className="product-list-header">My Products</h1>
      <section className="products-container">
        <div className="product-list-product-wrapper">
          {filteredList.map((product) => (
            <Product
              key={product.id}
              product={product}
              addItemToCheckout={addItemToCheckout}
            />
          ))}
        </div>
      </section>
    </div>
  );
};

export default ProductList;
```

I cut out a lot of unneeded noise, but that's still plenty happening in this component to render both a list of all the products and two different filtering options, so let's talk through what's happening in this code snippet.

The first `section` tag is wrapping our various filters:

- One filtering option users have is to filter products by department, hence the array-based state variable titled `filtersByDepartment`,
- And the other filtering option is to filter products by brand name: `filtersByBrand`.

Each of these arrays of data produces a set of checkboxes that users can check to narrow down the list of products being displayed on screen at any one time.

Which brings us to the final `section` of the JSX where the `filteredList` variable is being rendered - that variable is a list of all the products that fit into the selected filter criteria.

Also take note of the classes attached to many of these elements: `product-list-container`, `filter-wrapper`, `product-container` - they'll come into play in the next section when we write the CSS.

> I've omitted the filtering logic that's also in this component because it isn't central to illustrating the point in this post.

And that's what's going on in this component.

## Add CSS Grid to Our HTML

With our JSX (or HTML) set up, we can get to the CSS portion of this blog post. This is the entirety of the CSS contained in our `ProductList.css` file - there are some things that are being inherited from other global CSS - things like `background-color` or `font-size`, but by and large, this is all you'll need to achieve the desired effect.

**`ProductList.css`**

```css
/* styling for the whole product list container, including the filters on the side */
.product-list-container {
  display: grid;
  grid-template-rows: 55px calc(100vh - 55px);
  grid-template-columns: 220px auto;
  grid-template-areas:
    'filter header'
    'filter products';
}

/* styling for just the main list of products displayed on screen */
.product-list-header {
  display: flex;
  align-items: center;
  justify-content: center;
  grid-area: header;
}

.products-container {
  grid-area: products;
  overflow: auto;
}

.product-list-product-wrapper {
  display: flex;
  flex-wrap: wrap;
  margin: auto;
}

/* styling for the filters */
.filter-wrapper {
  padding: 10px;
  background-color: #ffcfae;
  grid-area: filter;
  height: 100vh;
}

.filter-title {
  font-weight: 600;
}

.filter-data {
  display: flex;
  flex-direction: column;
  flex-wrap: wrap;
  border-top: 2px solid #012025;
}

.filter-item {
  margin: 8px 16px;
}

.filter-checkbox {
  margin-left: 10px;
}
```

You'll notice as you look through the CSS that I have two distinct sets of classes in here:

- Ones that are `product` based
- And ones that are `filter` based.

It should be fairly self-explanatory what each set of classes is responsible for styling in the JSX; aside from the `product-list-container` that styles the whole page (and lays out our overall grid), `product` classes are for the product cards displayed on the page.

[![Hardware Handler products](https://www.paigeniedringhaus.com/static/fc96c83f1ddf85f22701a603b5801e1f/1e043/products.png "Hardware Handler products")](/static/fc96c83f1ddf85f22701a603b5801e1f/b2313/products.png)

_An example of the page title and product cards displayed on the product list page_

And `filter` classes are for the filters on the left hand side.

[![Hardware Handler filters on product list page](https://www.paigeniedringhaus.com/static/8831b229d97739be0a24f5c8a397fec1/d68e4/filters.png "Hardware Handler filters on product list page")](/static/8831b229d97739be0a24f5c8a397fec1/d68e4/filters.png)

_The filters to filter down the products displayed on the page_

Let's talk about some of the individual CSS classes in the code snippet now.

**`product-list-container`**

```css
.product-list-container {
  display: grid;
  grid-template-rows: 55px calc(100vh - 55px);
  grid-template-columns: 220px auto;
  grid-template-areas:
    'filter header'
    'filter products';
}
```

The `product-list-container` CSS at the beginning of the file is the first thing we need to focus on here because it's where the CSS Grid is defined and laid out.

- **`display: grid`** : Just like when using CSS Flexbox, the first thing we must do to let our CSS know we intend to use Grid instead of `display: flex` or `display: block`, is set the `display` property to `grid`
- **`grid-template-rows: 55px calc(100vh - 55px)`**: Next, we define the rows we want our grid to have.
  - If you look at my page's layout below the nav bar which is always present, there's the page header that says "My Products" and then the body of the rows of product cards, so in our case there really only needs to be two rows: one for the header and another for the list of products.
  - Looking at the code, I gave the page title `55px` of room and then the cards the remaining portion of the viewport to the cards by using the CSS **[`calc`](https://developer.mozilla.org/en-US/docs/Web/CSS/calc)** function to subtract the `55px` from whole height of the [viewport](https://css-tricks.com/fun-viewport-units/): `100vh`.
- **`grid-template-columns: 220px auto`** : As with defining rows, CSS Grid also allows us to define columns, and since this page has a side bar and a main content portion, 2 columns should do just fine. The sidebar portion will be `220px` wide and the rest of the space will be taken up by the products, so `auto` will suffice here.
- **`grid-template-areas: 'filter header' 'filter products'`** : This last line is an interesting one. While there's multiple ways to lay out CSS Grid, one of the handiest features is the ability to use **[`grid-template-areas`](https://developer.mozilla.org/en-US/docs/Web/CSS/grid-template-areas)** which let you name grid areas and then lay them out according to those names.
  - Oftentimes `grid-row` and `grid-column` will suffice to place things where they belong on the grid, but in this instance, `grid-template-areas` makes it really easy to lay out everything exactly as it should be on the page.

As you can see from looking at the code, the grid element named `filter` will take up **both grid rows** , and the **first grid column** (the one that is `220px` wide).

The grid element `header` will take up just the **first row** of the grid (the `55px` high row), and the **second column** , and the grid element `products` will take up the **second row** and **second column** of the grid (all the remaining space, basically).

And now that the CSS grid is laid out and its columns and rows are defined, all that's left is to name the classes to match the grid elements defined.

**`product-list-header`**

```css
.product-list-header {
  display: flex;
  align-items: center;
  justify-content: center;
  grid-area: header;
}
```

We'll just work our way down the CSS, I think that's easiest here, and the first class we'll focus on after `product-list-container` is `product-list-header`. The name should give you a hint of what this class is for: it wraps the "My Products" title of the page.

In addition to a little CSS Flexbox magic to align the "My Products" page title both horizontally and vertically within its space on the page, it also gets assigned a **[`grid area`](https://developer.mozilla.org/en-US/docs/Web/CSS/grid-area)**.

- **`grid-area: header`** : The `grid-area` is the property that tells an element where it should live within the parent grid container, so by giving this element the named grid area of `header`, it knows it should fit into the `header` space as defined in the `grid-template-area` in the `product-list-container` class.

**`products-container`**

```css
.products-container {
  grid-area: products;
  overflow: auto;
}
```

The next class we encounter on the way down the CSS file is the `products-container` class. This element only needs two CSS properties:

- **`grid-area: products`** : Once more, we assign this element the `grid-area` name of `products`, telling it that it should occupy all the space in the grid defined as `products`.
- **`overflow: auto`** : We also need to add the property of `overflow: auto` so that the list of products will scroll independently of the other elements on the page: the `header` and the `sidebar`.

> **overflow: auto;** is a key ingredient to our fixed sidebar remaining fixed.
> 
> Without it, if there's more products than can be displayed at once on screen, the products will still wrap and take up all the space they need but the whole grid will scroll to display them (including scrolling beyond the space the sidebar occupies). No bueno.
> 
> Just don't forget to add this property to the `products-container`.

**`filter-wrapper`**

```css
.filter-wrapper {
  padding: 10px;
  background-color: #ffcfae;
  grid-area: filter;
  height: 100vh;
}
```

Then we have to skip down through a few classes that exist to organize the products so they lay out nicely in rows on the page, regardless of the amount of items, until we come to the `filter-wrapper` class.

There's some standard `padding`, and `background-color` CSS properties at work, but the two properties to focus on are:

- **`grid-area: filter`** : Our final `grid-area` designation has arrived. This class gets the `filter` property, so it will know to fill up the left hand side of the screen for both grid rows.
- **`height: 100vh`** : The viewport height (`vh`) unit makes another appearance for this element. Setting the filter element's height to `100vh` ensures it will always go down to the bottom of the page (and look like a true sidebar) no matter how many actual filters are available or how far down the product list a user scrolls.

Then after this class there's a few additional ones to lay out all the filter checkboxes in a nice vertical row with some decent spacing between them.

Again, not as important to this particular post as the four classes highlighted above.

And that's it: a fixed sidebar with other elements on the page that can scroll freely.

---

## Conclusion

Building a sample application for users taking my course about how to modernize React applications helped me learn some cool new web development techniques along the way, not the least of which was how to build a page with a static sidebar and a scrolling center section using CSS Grid.

While this tutorial used CSS Grid to build a fixed sidebar, it could be applied to so many different situations that call for something similar: a table of contents for a blog post, a set of store details alongside a map of their locations, food items already in a cart as a user adds more to the list - the possibilities go on.

Thanks for reading. I hope you enjoyed seeing how just a few CSS Grid commands can eliminate the need to break the DOM flow with CSS like `position: fixed` and still give fine grained control. It takes a little practice to get the hang of CSS Grid, but once you do, I guarantee you won't want to go back. I can't imagine doing web development today without CSS Flexbox and CSS Grid at my fingertips.

If you’d like to make sure you never miss an article I write, sign up for my newsletter here: https://paigeniedringhaus.substack.com

> If you enjoyed this post, you may also be interested in checking out my full course **["The newline Guide to Modernizing an Enterprise React App"](https://www.newline.co/courses/newline-guide-to-modernizing-an-enterprise-react-app)**.

In 10 modules and 54 lessons, I cover all the things I learned while building applications at The Home Depot. I deep dive into building and maintaining large, mission-critical React applications - because at big companies like this, it's about so much more than just making the code work.

From tooling and refactoring, to testing and design system libraries, there's a ton of material and hands-on practice here to prepare any React developer to build software that lives up to today's high standards. I hope you'll check it out.

---

## Further References & Resources

- [CSS Grid](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Grid_Layout) Mozilla docs
- CSS-Tricks: [A Complete Guide to Grid](https://css-tricks.com/snippets/css/complete-guide-grid/)
- [The newline Guide to Modernizing an Enterprise React App](https://www.newline.co/courses/newline-guide-to-modernizing-an-enterprise-react-app) course
- [CSS Flexbox](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Flexible_Box_Layout/Basic_Concepts_of_Flexbox) Mozilla docs
- CSS-Tricks: [A Complete Guide to Flexbox](https://css-tricks.com/snippets/css/a-guide-to-flexbox/)