Skip to content

Load and display article data

We are now ready to load articles from the database and display them on the page.

To do this, we will use the prisma client to fetch all articles from the database first.

Open app/routes/news.$category.tsx.

Take a look at the loader function that we created in this file, and take a moment to remind yourself what it does:

app/routes/news.$category
import { invariant } from '@epic-web/invariant'
import { type LoaderFunctionArgs, json } from '@remix-run/node'
import { useLoaderData } from '@remix-run/react'
import { toTitleCase } from '~/utils/stringUtils.ts'
export async function loader({ params }: LoaderFunctionArgs) {
const { category } = params
invariant(typeof category === 'string', 'Category not found')
const categoryTitle = toTitleCase(category)
return json({ categoryTitle })
}
const WireframeBlock = () => {
return (
<div className="h-72 animate-pulse rounded-lg bg-gray-200 dark:bg-gray-700" />
)
}
export default function NewsCategoryPage() {
const { categoryTitle } = useLoaderData<typeof loader>()
return (
16 collapsed lines
<div className="container py-16">
<h2 className="mb-8 text-h2">{categoryTitle}</h2>
<div className="grid grid-cols-2 gap-6 md:grid-cols-3 lg:grid-cols-5">
<WireframeBlock />
<WireframeBlock />
<WireframeBlock />
<WireframeBlock />
<WireframeBlock />
<WireframeBlock />
<WireframeBlock />
<WireframeBlock />
<WireframeBlock />
<WireframeBlock />
</div>
</div>
)
}

Add an import to the prisma client at the top of the file:

app/routes/news.$category.tsx
import { invariant } from '@epic-web/invariant'
import { json, type LoaderFunctionArgs } from '@remix-run/node'
import { useLoaderData } from '@remix-run/react'
import techIcon from '~/assets/png/news-categories/tech-logo@2x.png'
import { prisma } from '~/utils/db.server.ts'

Next, let’s fetch all articles from the database using the prisma client inside the loader function, and then export them as JSON:

app/routes/news.$category.tsx
5 collapsed lines
import { invariant } from '@epic-web/invariant'
import { json, type LoaderFunctionArgs } from '@remix-run/node'
import { useLoaderData } from '@remix-run/react'
import { toTitleCase } from '~/utils/stringUtils'
import siteLogo from '~/assets/png/epic-news-logo@2x.png'
import techIcon from '~/assets/png/tech-logo@2x.png'
import { prisma } from '~/utils/db.server.ts'
export async function loader({ params }: LoaderFunctionArgs) {
const { category } = params
invariant(typeof category === 'string', 'No category provided')
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 })
}

Now that we have fetched all the articles from the database, let’s display them on the page.

At first, we will just display the title and category of each article within the grid that we populated with wireframe blocks earlier.

  1. Firstly, delete the WireframeBlock components from the page, as we don’t need them anymore:

    app/routes/news.$category.tsx
    23 collapsed lines
    import { invariant } from '@epic-web/invariant'
    import { type LoaderFunctionArgs, json } from '@remix-run/node'
    import { useLoaderData } from '@remix-run/react'
    import { prisma } from '~/utils/db.server.ts'
    import { toTitleCase } from '~/utils/stringUtils.ts'
    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 })
    }
    const WireframeBlock = () => {
    return (
    <div className="h-72 animate-pulse rounded-lg bg-gray-200 dark:bg-gray-700" />
    )
    }
    export default function NewsCategoryPage() {
    const { categoryTitle } = useLoaderData<typeof loader>()
    return (
    <div className="container py-16">
    <h2 className="mb-8 text-h2">{categoryTitle}</h2>
    <div className="grid grid-cols-2 gap-6 md:grid-cols-3 lg:grid-cols-5">
    <WireframeBlock />
    <WireframeBlock />
    <WireframeBlock />
    <WireframeBlock />
    <WireframeBlock />
    <WireframeBlock />
    <WireframeBlock />
    <WireframeBlock />
    <WireframeBlock />
    <WireframeBlock />
    </div>
    </div>
    )
    }
  2. Next, we will return allArticles from the useLoaderData function that we now have access to, and loop through the articles using a map function.

    app/routes/news.$category.tsx
    23 collapsed lines
    import { invariant } from '@epic-web/invariant'
    import { type LoaderFunctionArgs, json } from '@remix-run/node'
    import { useLoaderData } from '@remix-run/react'
    import { prisma } from '~/utils/db.server.ts'
    import { toTitleCase } from '~/utils/stringUtils.ts'
    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 })
    }
    export default function NewsCategoryPage() {
    const { categoryTitle, allArticles } = useLoaderData<typeof loader>()
    return (
    <div className="container py-16">
    <h2 className="mb-8 text-h2">{categoryTitle}</h2>
    <div className="grid grid-cols-2 gap-6 md:grid-cols-3 lg:grid-cols-5">
    {allArticles.map(article => (
    <div key={article.id}>
    <h3>{article.title}</h3>
    <p>{article.category?.name || 'General News'}</p>
    </div>
    ))}
    </div>
    </div>
    )
    }
  3. Save the file and check the browser to see the unstyled article data displayed on the page:

    Unstyled article data

  4. Let’s make them a little clearer by adding a background and some padding to each article β€˜card’:

    app/routes/news.$category.tsx
    23 collapsed lines
    import { invariant } from '@epic-web/invariant'
    import { type LoaderFunctionArgs, json } from '@remix-run/node'
    import { useLoaderData } from '@remix-run/react'
    import { prisma } from '~/utils/db.server.ts'
    import { toTitleCase } from '~/utils/stringUtils.ts'
    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 })
    }
    export default function NewsCategoryPage() {
    const { categoryTitle, allArticles } = useLoaderData<typeof loader>()
    return (
    <div className="container py-16">
    <h2 className="mb-8 text-h2">{categoryTitle}</h2>
    <div className="grid grid-cols-2 gap-6 md:grid-cols-3 lg:grid-cols-5">
    {allArticles.map(article => (
    <div className="bg-red-900 p-4" key={article.id}>
    <h3>{article.title}</h3>
    <p>{article.category?.name || 'General News'}</p>
    </div>
    ))}
    </div>
    </div>
    )
    }
  5. Save the file and check the browser to see the styled article data displayed on the page:

    Article data with background

In this guide, we learned how to:

  • Use the select option in Prisma to only return the fields we need from a database.
  • Load real articles from the database and display them on the page.
  • Loop through these real articles using JavaScript’s map function and display them on the page.
  • Style the article cards in line with your brand design.

In the next guide, we will learn how to filter articles by the correct category.