A walkthrough of a simple filter UI driven by Craft CMS data, Gatsby, and React hooks.
Fetching data
Start with a GraphQL query that pulls categories and posts from Craft:
{
craft {
categories: categories(group: "newsCategories") {
... on craft_newsCategories_Category {
id
title
slug
}
}
allPosts: entries(section: "news") {
... on craft_news_news_Entry {
id
title
newsCategory {
slug
}
}
}
}
}
Building the filter
Use useState to hold the filtered list and the active filter:
const [posts, setPosts] = useState(allPosts)
const [selected, setSelected] = useState("all")
const filterPosts = value => {
let posts = allPosts
if (value !== "all") {
posts = allPosts.filter(post =>
post.newsCategory.find(category => category.slug === value)
)
}
setSelected(value)
setPosts(posts)
}
Optional extras
Framer Motion. Wrap the filtered list in AnimatePresence for clean transitions as items enter and leave.
Permalinks. Generate category pages in gatsby-node.js so each filter has its own URL, and apply the filter on mount with useEffect.
Where this pattern fits
Blog archives, product filters, team directories, location search — same shape. For larger result sets, pair it with something like react-paginate.
Sources
More from The Journal
Should you be worried about the rise in high-severity Craft CMS security updates?
Craft has had a huge increase in security patches this year. But don't be alarmed, it's a sign of the times and that Craft is dealing with things responsibly.
Can AI text help my Craft CMS website's performance?
Can you benefit from having AI create new content for your website? Let's dive in!
Redirects in Craft CMS without a plugin
Craft CMS now has built in redirects abilities, so you may not need a plugin for them in 2026.