7 days of unlimited WordPress themes, plugins & graphics - for free!* Unlimited asset downloads! Start 7-Day Free Trial
  1. Web Design
  2. CSS Grid Layout

How to Build a Filterable Thumbnail Layout With CSS Grid, Flexbox, and JavaScript

Scroll to top
Read Time: 9 mins

In this tutorial, we’re going to take a bunch of photos and turn them into a filterable thumbnail layout. We’ll combine all the latest CSS goodies (CSS Grid, flexbox, and CSS variables) along with some custom JavaScript to build an amazing demo!

Here’s what we’ll be creating:

Be sure to check the demo on a large screen (>900px) because at that point the magic happens! Without further ado, grab a cup of coffee and let’s get cracking!

1. Begin With the Page Markup

We’ll start with a .container which holds the .toolbar element and a list of photos:

The toolbar layout will look like this:

The toolbar layout

Inside it we’ll place two elements:

  • The search box which allows us to search for a specific photo
  • a list with three options which determine the thumbnail layout. By default the photos appear in grid view, but we can switch to the list view by clicking on the icon in the right corner. Furthermore, each time we are in the grid view, we have the option to change the number of photos which appear per row. To do this, we’ll use a range slider. 

Here’s the associated markup for all that:

Inside the image list we’ll place twelve Unsplash photos. Each photo comes with its description as well as the name of its owner. Here’s what it will look like with some basic styling (which we’ll come to shortly):

The image layout

Here’s the markup for a single photo:

It’s important to note that the image list will always contain the image-list class. In addition, it will also receive either the grid-view or the list-view class like this: <ol class="image-list grid-view">...</ol>

Its second class will depend on the layout view selected by the user. More on that in the upcoming sections. 

2. Define Some Basic Styles

We first set up a few CSS variables and some reset styles:

Most importantly, pay attention to the value of the minRangeValue variable. Its value (280px) matches the default value of the range slider. 

Remember the markup for our slider: <input type="range" min="180" max="380" value="280">. Later on we’ll use that value for setting the minimum width of our photos.

Note: for simplicity I won’t walk through all the CSS rules in the tutorial. You can check the rest of them by clicking the CSS tab of the demo project.

Styling the Toolbar

As a next step, we’ll style the toolbar. Here are the key points regarding this element:

  • We use flexbox to layout its contents.
  • Each time a user selects a layout (grid or list), the corresponding button marks as active and receives a darkgreen border. In addition, we disable it.
  • The range slider appears only when the grid view is active and for screens which have a minimum width of 901px.

The important parts of the corresponding styles are shown below:

Styling the Image List

As already mentioned, the image list layout will depend on the layout selected by the user. In any case, we’ll take advantage of CSS Grid to build this layout.

Before doing so, let’s apply a few generic styles to the image list:

In grid view, the lists items will be divided into repetitive columns/cells with gutters between them:

The grid view

The number of items that will appear per row depends on the screen size. Initially, each item will have a minimum width of 280px and a maximum width that matches its container width. But as we’ll see later, we’ll add a little bit of interactivity and give users the option to modify the minimum width. For this reason we won’t hardcode its value but instead store it in the minRangeValue variable.

We’re able to produce this truly responsive layout by combining the minmax() CSS function with CSS Grid. Here’s how the aforementioned requirements are translated in terms of styles:

In list view, the list items will have the default block-level behavior:

The list view

Inside them, the image will have a fixed width of 150px and its description will cover the rest of the available space (1fr). Plus, both elements will be vertically centered with a gutter between them.

The related styles:

To understand more about how minmax() works with CSS Grid Layout, here’s a beginner’s tutorial:

3. Toggle Between the List and Grid Views

Each time a user clicks on the desired pattern for content presentation, we do two things:

  • Add a darkgreen border to the active button.
  • Check which type of view is selected by the user.

If the user selects the grid view, we display the range slider and ensure that the image list contains the grid-view class and not the list-view class.

On the other hand, if the user selects the list view, we hide the range slider and ensure that the image list contains the list-view class and not the grid-view class.

The required JavaScript code:

4. Update CSS Variables Through JavaScript 

We have already discussed that the initial value of the range slider is 280px and matches the value of the minRangeValue variable. Plus, its minimum value is 180px while its maximum is 380px.

We need to keep track of the slider value changes and update the minRangeValue variable accordingly. This will make our grid view layout flexible as each row won’t contain a fixed number of columns.

The JavaScript code that will do the trick makes use of the input event:

To understand this code, open your browser tools and update the slider value. You’ll notice that the html element receives an inline style which overwrites the property value set through CSS:

Updating CSS variables through JavaScript

5. Build the Search Functionality

Currently we only have twelve images, but image a scenario where we have dozens of images. In such a case, it would be really nice if users had the ability to search for specific photos.

So, let’s go ahead and build a custom search component like this:

Find images which have the word mountain in their description

Note that it will only work for image descriptions:

Search only for image description

As a first step we do the following:

  • Iterate through all photos. 
  • For each photo we find, we initialize an object literal with two properties. 
  • The first property is the id with an increment number which is unique for each object. The second property is the text which stores the target photo description. 
  • Store all objects in an array.

The JavaScript code that implements this functionality:

One thing to note is that our counter starts from 1 and not from 0. We did this intentionally because this will help us easily target the desired elements later on.

User Input

Next, each time a user types something in the search input, we do the following:

  1. Hide all photos.
  2. Grab the search query.
  3. Check if there are array elements (objects) which include the search query in their text property value.
  4. Show the elements that fulfill the requirement above.
  5. Print their number in the screen. If there aren't any elements print 0.

The required JavaScript code:

And the CSS class used here:

Note: There are different methods we can use to prevent the callback function from running every time a user releases a key (keyup event). Although beyond the scope of this tutorial, an effective solution might be to use Lodash’s debounce function.

Tip: If you want to make a case-insensitive search, one simple solution is to replace the filteredArray constant from the steps above (// 3) with this:


That’s it folks! It was indeed a long journey, yet I hope you learned some new things and you enjoyed the demo we built here. Play around with it and let me know if you have any questions.

As always, thanks for reading!

More CSS Layout Projects on Tuts+

Did you find this post useful?
Want a weekly email summary?
Subscribe below and we’ll send you a weekly email summary of all new Web Design tutorials. Never miss out on learning about the next big thing.
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.