Skip to content

Filter articles by category

Our news index page is looking great, but it’s missing one key feature: the ability to filter articles by category.

If you try clicking between the different news categories, you’ll notice that the URL and page title changes, but the articles don’t update:

Category filter not working

In this guide, we’ll update the loader function in our app/routes/news.$category.tsx file to filter articles by category and if they are published or not.

At the moment, our loader function is fetching all articles from the database:

app/routes/news.$category.tsx
export async function loader({ params }: LoaderFunctionArgs) {
const { category } = params
invariant(typeof category === 'string', 'Category not found')
const categoryTitle = toTitleCase(category)
const allArticles = await prisma.article.findMany({
select: {
id: true,
title: true,
category: { select: { name: true } },
images: { select: { id: true } },
},
})
return json({ categoryTitle, allArticles })
}

Let’s fix this so that only articles in the specified category are retrieved.

  1. Open the app/routes/news.$category.tsx file.

  2. Let’s start by renaming allArticles to something more suitable:

    app/routes/news.$category.tsx
    export async function loader({ params }: LoaderFunctionArgs) {
    const { category } = params
    invariant(typeof category === 'string', 'Category not found')
    const allArticles = await prisma.article.findMany({
    const filteredArticles = await prisma.article.findMany({
    select: {
    id: true,
    title: true,
    category: { select: { name: true } },
    images: { select: { id: true } },
    },
    })
    return json({ category, allArticles })
    return json({ category, filteredArticles })
    }
  3. Next, we need to actually implement the filtering logic.

    We’ll use Prisma’s where option to filter articles by category.

  4. We already have access to the category parameter, so let’s add a where option to our query:

    app/routes/news.$category.tsx
    export async function loader({ params }: LoaderFunctionArgs) {
    const { category } = params
    invariant(typeof category === 'string', 'Category not found')
    const categoryTitle = toTitleCase(category)
    const filteredArticles = await prisma.article.findMany({
    where: {
    category: {
    slug: category, // Retrieves only articles in the specified category
    },
    },
    select: {
    id: true,
    title: true,
    category: { select: { name: true } },
    images: { select: { id: true } },
    },
    })
    return json({ categoryTitle, filteredArticles })
    }
  5. Finally, we need to update the useLoaderData hook in our NewsIndexPage component to return the filteredArticles data, and map through this in place of the old allArticles array:

    app/routes/news.$category.tsx
    export default function NewsCategoryPage() {
    const { category, filteredArticles } = useLoaderData<typeof loader>()
    return (
    <div className="container py-8 lg:py-16">
    <h2 className="mb-16 text-h2">{category}</h2>
    <div className="grid grid-cols-1 gap-6 md:grid-cols-4 xl:grid-cols-3">
    {filteredArticles.map(article => {
    return (
    <ArticleCard
    key={article.id}
    title={article.title}
    categoryTitle={categoryTitle}
    imageId={article.images[0]?.id}
    />
    )
    })}
    </div>
    </div>
    )
    }
  6. Now, if you navigate to the news index page and click on a category, you should see the articles update based on the selected category:

    Category filter working

In this guide, we have:

  • Learned about the where option in Prisma to filter database records based on specific criteria.
  • Updated the loader function in our app/routes/news.$category.tsx file to filter articles by category and if they are published or not.
  • Updated the useLoaderData hook in our NewsIndexPage component to return the filteredArticles data and map through this in place of the old allArticles array.
  • Checked the filters in the browser to ensure they are working as expected.
  • Further enhanced the news index page.

In the next guide, we will add an extra page to display the content of individual articles.