In this guide, youβll style the single article page in Epic News.
As with the other tutorials in this section, there is nothing new here that you havenβt learned before.
This will be down to you to display and style the rest of the article content on the page.
Challenge
Using your knowledge of Tailwind CSS and JSX components, style the single article to make it look clean, professional and appealing.
You should display the following information:
Article title
Author name
The first image from the article (you can display more if you like)
If there is no main image from the article, display a fallback image of your choice (perhaps the site logo?)
Article content
When you are ready, check out one possible solution below and compare it with your own.
Because an article might not be found, Iβve extracted the actual article display into a new component called SingleArticle
.
This will make it easier to work with the ternary statement in our ArticlePage
component.
Iβve saved this component in a new file at app/components/organisms/SingleArticle.tsx
Eventually (in the next solution step), I will use this component in the single article page.
This component will be responsible for displaying the article title
, content
, author name
, category
and images
.
import { Link } from '@remix-run/react'
import { FiArrowLeft } from 'react-icons/fi'
import { toTitleCase } from '#app/utils/stringUtils.js'
import siteLogo from '~/assets/svg/site-logo.svg'
import { getArticleImgSrc } from '~/utils/misc.js'
interface SingleArticleProps {
export default function SingleArticle ({ article }: SingleArticleProps ) {
const mainImage = article . images [ 0 ]
const imageSrc = article . images . length
? getArticleImgSrc ( mainImage . id )
const categoryTitle = toTitleCase ( article . category ?. name || '' )
const categoryIcons : { [ key : string ]: JSX . Element } = {
Business: < MdOutlineBusinessCenter size = { 20 } className = "text-violet-300" /> ,
Entertainment: < MdOutlineTheaters size = { 20 } className = "text-violet-300" /> ,
Technology: < MdOutlineDesktopMac size = { 20 } className = "text-violet-300" /> ,
< MdOutlineNewspaper size = { 20 } className = "text-violet-300" />
< div className = "container py-16" >
< div className = "lg:w-2/3" >
className = "group flex items-center gap-2 pb-4 text-muted-foreground transition hover:text-foreground"
< FiArrowLeft className = "transition group-hover:-translate-x-1" /> Back
< h2 className = "pb-8 text-h2" > { article . title } </ h2 >
className = { `relative h-[18rem] object-cover md:h-[23rem] lg:h-[28rem]` }
< div className = "absolute inset-0" >
className = "h-full w-full rounded-t-lg object-cover"
< div className = "flex justify-between gap-4 pt-4" >
< div className = "flex items-center gap-2" >
{ categoryIcons [ categoryTitle ] }
< p className = "text-sm text-violet-300" > { categoryTitle } </ p >
< span className = "text-sm text-muted-foreground" >
< div className = "whitespace-break-spaces pt-16 text-lg leading-loose" >
This is a lot of code! But donβt worry, itβs just a lot of JSX. The logic is simple: weβre displaying the article title, content, author name, category and images.
Take the time to understand how the component works, and how it uses the getArticleImgSrc
utility function to get the image source.
With this in place, I can make two simple changes to the single article page.
Open the app/routes/article.$articleId.tsx
file
Add the changes highlighted in green below:
import { invariant } from '@epic-web/invariant'
import { type LoaderFunctionArgs , json } from '@remix-run/node'
import { useLoaderData } from '@remix-run/react'
import SingleArticle from '#app/components/organisms/SingleArticle.tsx'
import { prisma } from '~/utils/db.server.ts'
export async function loader ({ params }: LoaderFunctionArgs ) {
const { articleId } = params
invariant ( typeof articleId === 'string' , 'No article ID provided' )
// Fetch the article by ID
const article = await prisma . article . findUnique ({
where: { id: articleId },
category: { select: { name: true } },
owner: { select: { name: true } },
images: { select: { id: true } },
const ArticleNotFound = () => {
< div className = "container flex h-full flex-1 flex-col items-center justify-center" >
< h2 className = "pb-8 text-center text-h2" > No article found π€ </ h2 >
< p className = "text-center text-xl" >
Please check the article ID in your browser and try again.
export default function ArticlePage () {
const { article } = useLoaderData < typeof loader >()
return article ? < SingleArticle article = { article } /> : < ArticleNotFound />
With this code in place, my single article page is fully styled:
3. Modify single article page
In this step, we have:
Extracted each field relevant to display on the the single article page
Provided a fallback screen to show if the user tries to access an article that doesnβt exist
Styled the single article page in Epic News with Tailwind CSS
In the next section, we will ensure that admins can edit and publish articles in Epic News, while regular users can only edit the content of their own.