Load and display article data
Introduction
Section titled βIntroductionβ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.
Updating the loader function
Section titled βUpdating the loader functionβ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:
import { invariant } from '@epic-web/invariant'import { type LoaderFunctionArgs, data, useLoaderData } from 'react-router'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 data({ 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> )}Import the prisma client
Section titled βImport the prisma clientβAdd an import to the prisma client at the top of the file:
import { invariant } from '@epic-web/invariant'import { type LoaderFunctionArgs, data, useLoaderData } from 'react-router'import techIcon from '~/assets/png/news-categories/tech-logo@2x.png'import { prisma } from '~/utils/db.server.ts'Fetch all database articles
Section titled βFetch all database articlesβNext, letβs fetch all articles from the database using the prisma client inside the loader function, and then export them as JSON:
3 collapsed lines
import { invariant } from '@epic-web/invariant'import { type LoaderFunctionArgs, data, useLoaderData } from 'react-router'import { toTitleCase } from '~/utils/stringUtils'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 data({ categoryTitle, allArticles })}Display article data
Section titled βDisplay article dataβ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.
-
Firstly, (if you still have them), delete the
WireframeBlockcomponents from the page, as we donβt need them anymore:app/routes/news.$category.tsx 22 collapsed linesimport { invariant } from '@epic-web/invariant'import { type LoaderFunctionArgs, data, useLoaderData } from 'react-router'import { prisma } from '~/utils/db.server.ts'import { toTitleCase } from '~/utils/stringUtils.ts'export async function loader({ params }: LoaderFunctionArgs) {const { category } = paramsinvariant(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 data({ 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>)} -
Next, we will return
allArticlesfrom theuseLoaderDatafunction that we now have access to, and loop through the articles using amapfunction.Letβs use the
mapfunction to loop through theallArticlesarray and render adivfor each article:app/routes/news.$category.tsx 22 collapsed linesimport { invariant } from '@epic-web/invariant'import { type LoaderFunctionArgs, data, useLoaderData } from 'react-router'import { prisma } from '~/utils/db.server.ts'import { toTitleCase } from '~/utils/stringUtils.ts'export async function loader({ params }: LoaderFunctionArgs) {const { category } = paramsinvariant(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 data({ 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>)} -
Save the file and check the browser to see the unstyled article data displayed on the page:

-
Letβs make them a little clearer by adding a background and some padding to each article βcardβ:
app/routes/news.$category.tsx 22 collapsed linesimport { invariant } from '@epic-web/invariant'import { type LoaderFunctionArgs, data, useLoaderData } from 'react-router'import { prisma } from '~/utils/db.server.ts'import { toTitleCase } from '~/utils/stringUtils.ts'export async function loader({ params }: LoaderFunctionArgs) {const { category } = paramsinvariant(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 data({ 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} className="bg-red-900 p-4"><h3>{article.title}</h3><p>{article.category?.name || 'General News'}</p></div>))}</div></div>)} -
Save the file and check the browser to see the styled article data displayed on the page:

Summary
Section titled βSummaryβIn this guide, we learned how to:
- Use the
selectoption 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
mapfunction 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.