EXPERT TUTORIALS

How to Add Filters to Your Astro Blog

Published: December 27, 2023

blog-header-image

How to add filters to your Astro blog

Introduction

I’ve been working with the Astro web-framework for ~8 months and recently added new functionality to our site that allows for client side filtering of our insights section. This was a fun problem to solve and I wanted to share my approach for how I tackled this.

For those of you looking to implement this, please note we are using content collections with .mdx file types. We use the tag frontmatter as the basis for our filtering.

Step 1: Creating the filter buttons.

The first step was to create a component for our filter buttons. We gave this component an ID of blogFilter and passed the tag as a data attribute.

---
const {tag} = Astro.props
---
<button data-type={tag} class="text-primary bg-gray-200 p-2 mx-2" id="blogFilter">
    {tag}
</button>

Step 2: Get unique tags from our collection

In our index.astro file inside our insights collection folder, we then created an array with unique tags from all our insights.

These unique tags were used as another data attributed for the parent element holding our tag buttons.

The map function was used to loop through these unique tags and create a filter for each tag (form step 1).

---
const insightsPosts = await getCollection("insights", ({ data }) => {
  return data.draft !== true && new Date(data.date) < new Date();
});

const tags = insightsPosts.map((p) => p.data.tags).flat(); //Gets all tags from collection
const uniqueTags = [...new Set(tags)]; //Keeps only unique tags
---

<div class="flex flex-wrap gap-2 mb-2" id="tags" data-tags={uniqueTags}>
          {uniqueTags.map((tag) => <BlogFilter {tag} />)}
 </div>

Step 3: Add tags as classes to blog card components

I then edited our blog card component to include the tag as a class. You can get this data from the post frontmatter. Here’s a snippet of what ours looks like.

---
const {post} = Astro.props
const frontMatter = post.data
let tags = frontMatter.tags
tags = tags.join(" ")
---

<div class=`bg-primary rounded-lg ${tags}`>
    <a href={url}><img src={blogImage} alt="blog-hero"></img></a>
    <h3 class="pt-5 pl-5 text-lg">{title}</h3>
    <p class="text-accent pt-2 px-5 text-sm ">{date}</p>
    <p class = "px-5 py-5 text-sm">{description}</p>
</div>

Step 4: Client side Javascript

  • We need to create a function that will hide blogs with tags that have not been selected.
  • To do this, we get the data-type attribute for the button that was selected (this is the tag name we set in step 1).
  • We then get all of our tags from the parent element from the data-tag attribute we set.
  • This is converted into an array, and the selected tag is removed from the array. This gives us a list of tags to hide.
  • We loop through our array of tags to hide and select all elements with these tags (using the getElementsByClassName method). These then have their display property set to none.
  • We then select all elements with our selectedTag class, loop through these and set display to block
  • Once this function is defined, the final step is to add an event listener to our blogFilter buttons and call the function on a click event.
<script>
  function hideBlogs(evt) {
    let selectedTag = evt.currentTarget.getAttribute("data-type");
    let tagElement = document.getElementById("tags");
    let tags = tagElement.dataset.tags;
    tags = tags.split(",");
    tags.splice(tags.indexOf(selectedTag), 1);

    tags.forEach((t) => {
      const elementsToHide = document.getElementsByClassName(t);
      for (let i = 0; i < elementsToHide.length; i++) {
        elementsToHide[i].style.display = "none";
      }
    });

    let elementsToShow = document.getElementsByClassName(selectedTag);
    for (let i = 0; i < elementsToShow.length; i++) {
      elementsToShow[i].style.display = "block";
    }
  }

  // Event listener for the button click
  const input = document.querySelectorAll("#blogFilter");
  input.forEach((e) => {
    e.addEventListener("click", hideBlogs);
  });
</script>

Share Article

Related Posts