Filtering Gatsby content by Sanity categories and tags

6 replies
Last updated: Jul 22, 2020
Hi! I'm working on my first Gatsby project, for a conference, and would like to create a filtering system for the speakers. I need it to be able to filter both sanity categories and tags. Similar to how they've done it here: https://ffconf.org/talks/ (click the "Filter Talks" button).Can anyone point me in the right direction/blogpost/video on how to do that?
AI Update

Creating a client-side filtering system for speakers with categories and tags in your Gatsby + Sanity project is definitely achievable! Here's how to approach this:

The Basic Approach

Since you're building this for a Gatsby site with a conference speaker list, you'll want to implement client-side filtering using React state management. The general pattern is:

  1. Query all speakers data (including their categories and tags) from Sanity via Gatsby's GraphQL layer
  2. Store the full dataset in your component
  3. Track selected filter values (checkboxes) in state
  4. Filter the displayed results based on selected filters

Getting Your Data from Sanity

First, structure your Sanity schema with categories and tags as references or arrays. Then in your Gatsby page, query the data using Gatsby's GraphQL integration with Sanity:

export const query = graphql`
  query {
    allSanitySpeaker {
      nodes {
        id
        name
        bio
        categories {
          title
          slug
        }
        tags {
          title
          slug
        }
      }
    }
  }
`

Implementing the Filter UI

For the filtering interface (similar to the ffconf example you linked), you'll want to:

  1. Extract unique categories and tags from your data
  2. Create checkbox inputs for each filter option
  3. Track selected filters in component state using useState
  4. Filter your results based on selections

Here's a basic React pattern using hooks:

const SpeakersPage = ({ data }) => {
  const [selectedCategories, setSelectedCategories] = useState([])
  const [selectedTags, setSelectedTags] = useState([])
  
  const speakers = data.allSanitySpeaker.nodes
  
  // Filter logic
  const filteredSpeakers = speakers.filter(speaker => {
    const categoryMatch = selectedCategories.length === 0 || 
      speaker.categories.some(cat => selectedCategories.includes(cat.slug))
    const tagMatch = selectedTags.length === 0 || 
      speaker.tags.some(tag => selectedTags.includes(tag.slug))
    
    return categoryMatch && tagMatch
  })
  
  // Render checkboxes and filtered results
}

Handling Checkbox State

Based on this helpful example from the Sanity community (which applies to Gatsby too since it's React-based), you can manage checkbox state like this:

const handleCategoryChange = (slug) => {
  setSelectedCategories(prev => 
    prev.includes(slug) 
      ? prev.filter(s => s !== slug)
      : [...prev, slug]
  )
}

There's also a CodeSandbox demo showing checkbox filtering that demonstrates the fundamentals perfectly.

Key Implementation Tips

Checkbox State Management: Use arrays to track selected filters and toggle them on/off when checkboxes are clicked.

Filter Logic: Decide whether you want AND logic (speaker must match ALL selected filters) or OR logic (speaker matches ANY selected filter). The ffconf example likely uses OR within each filter type (categories OR tags).

Performance: Since this is client-side filtering on a conference speaker list, performance should be fine. All data is already loaded at build time via Gatsby's GraphQL layer, which is one of the key benefits of Gatsby's integration with Sanity.

Clear Filters: Include a "Clear all" or "Reset" button to empty your filter state arrays.

Using Sanity's _id: When mapping over your filtered results, you can use Sanity's _id field as the React key prop since it's guaranteed to be unique.

Why Client-Side vs Server-Side?

For a conference speaker list, client-side filtering gives you instant, smooth interactions without page reloads. Since Gatsby pre-builds everything at build time, all your speaker data is already available in the browser. If you had thousands of speakers, you might consider server-side filtering with GROQ queries, but for a typical conference (dozens to hundreds of speakers), client-side is perfect.

This is essentially building a faceted search interface with checkboxes - a common pattern in directory sites and e-commerce filters. The key is maintaining filter state and using JavaScript's filter() and some() array methods to match against your criteria.

Show original thread
6 replies
put your speakers in a local state variable, something like:
const [players, setPlayers] = useState(data.players.edges.slice(0, 50))
then make a filter function that manipulates that state variable the way you want when a filter is clicked
also good to have your filters in another local state variable so you know which filters are active
const [filters, setFilters] = useState(null)
heres how my filter function looks for my use case
  function filter(teams) {
    if (filters === null) {
      const filtered = data.players.edges.filter(
        x => x.node.team._id === teams[0] || x.node.team._id === teams[1]
      )
      setPlayers(filtered)
      setFilters(teams)
    } else if (filters[0] === teams[0]) {
      setPlayers(data.players.edges.slice(0, 50))
      setFilters(null)
    } else {
      const filtered = data.players.edges.filter(
        x => x.node.team._id === teams[0] || x.node.team._id === teams[1]
      )
      setPlayers(filtered)
      setFilters(teams)
    }
  }
might not make total sense but a hint
πŸ™‚
Thank you, this is the kind of JS I need to get fluent in! I'm also getting some use out of the link
user L
posted earlier.
good luck! its totally worth the pain to learn this stuff :)
hehe thank you!

Sanity – Build the way you think, not the way your CMS thinks

Sanity is the developer-first content operating system that gives you complete control. Schema-as-code, GROQ queries, and real-time APIs mean no more workarounds or waiting for deployments. Free to start, scale as you grow.

Was this answer helpful?