Server/client communication with useLoaderData
Introduction
Section titled “Introduction”In the previous step, we learned about route parameters and how to use them to create dynamic routes in Remix. We then accessed this data on the server using the loader function.
In this next step we will:
- Pass the
categoryvalue to ourNewsCategoryPagecomponent and display it on the page with theuseLoaderDatahook. - Fix a TypeScript error by using the
invariantpackage to tell TypeScript that thecategoryTitlevalue should always be a string. - Complete a challenge to create a wireframe layout for the
NewsCategoryPageusing Tailwind CSS.
Passing data to the page
Section titled “Passing data to the page”With our previous step complete, we are now reading the category value from the URL 🎉. We can use this value to fetch the correct data for our page.
-
Open
app/routes/news.$category.tsx. -
Update the
loaderfunction so it returns thecategoryvalue in title-case as part of thedataresponse.app/routes/news.$category.tsx import { type LoaderFunctionArgs, data, useLoaderData } from 'react-router'import { toTitleCase } from '~/utils/stringUtils.ts'export async function loader({ params }: LoaderFunctionArgs) {const { category } = paramsconsole.log({ category })const categoryTitle = toTitleCase(category)return data({})return data({ categoryTitle })}export default function NewsCategoryPage() {const { categoryTitle } = useLoaderData<typeof loader>()return (<div className="container py-16"><h2 className="text-h2">Generic news category page</h2><h2 className="text-h2">{categoryTitle}</h2></div>)}
Checking the result
Section titled “Checking the result”Now, head back to your browser and click the links between the different news categories again.
This time, you should see the title of the category displayed in every page, but we didn’t need to hard-code a separate file for every one 🎉:

Fixing TypeScript errors
Section titled “Fixing TypeScript errors”Finally, let’s fix that TypeScript error we saw earlier.
We will do this using a package called Invariant. This package will help us to tell TypeScript that the categoryTitle value will always be a string.
First, import the package at the top of your news.$category.tsx file:
import { invariant } from '@epic-web/invariant'import { type LoaderFunctionArgs, data, useLoaderData } from 'react-router'import { toTitleCase } from '~/utils/stringUtils.ts'Next, inside your loader function, add the following check:
4 collapsed lines
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 })}
export default function NewsCategoryPage() {8 collapsed lines
const { categoryTitle } = useLoaderData<typeof loader>()
return ( <div className="container py-16"> <h2 className="text-h2">{categoryTitle}</h2> </div> )}The red line has disappeared, and TypeScript now knows that categoryTitle will always be a string.
Summary
Section titled “Summary”In this step, we:
- Read server data from the
loaderfunction using theuseLoaderDatahook, and displayed it on the page. - Fixed a TypeScript error by using the
invariantpackage to tell TypeScript that thecategoryTitlevalue will always be a string.