React Tailwind Project Card Component

Every developer needs a portfolio, and a portfolio is incomplete without showcasing projects.

Today, I'll share a project card for your portfolio that stands out from the typical components available online.

Project Card Component Features

  1. Project Image (cover)
  2. Project Stats
  3. Project Description
  4. Action Buttons

UI for Project Card

project card component ui

Component Dependency

In this component, I am using custom icons that can be found in the project's GitHub repository.

Tailwind Project Card Code

The component is built with TypeScript and accepts the following props:

  1. title
  2. description
  3. cover
  4. live preview (optional)
  5. GitHub link (optional)
  6. project type (client, new, cofounder, etc.)
  7. number of visitors
  8. number of stars
  9. number of sales
  10. age
  11. earning
  12. reviews
import { Earning, GithubDark, Likes, Preview, Star, Timer, } from '../../utils/icons' const IconText: React.FC<{ icon: string; text: string }> = ({ icon, text }) => ( <li className="flex gap-2"> <img src={icon} alt={text} className="w-[18px] md:w-5" /> <span className="text-sm">{text}</span> </li> ) interface ProjectCardProps { data: { title: string shortDescription: string priority: number cover: string livePreview?: string githubLink?: string visitors?: string earned?: string githubStars?: string ratings?: string numberOfSales?: string siteAge?: string type?: string } } const ProjectCard: React.FC<ProjectCardProps> = ({ data }) => { const { title, shortDescription, visitors, earned, ratings, githubStars, numberOfSales, livePreview, githubLink, siteAge, type, cover, } = data return ( <div className="rounded-[10px] border border-[#444444] bg-[#333333] p-5"> <div className="flex items-center justify-between gap-2"> <div className="flex-1"> <div className="flex flex-col flex-wrap gap-3 sm:flex-row sm:items-center"> <h3 className="text-xl font-medium sm:text-2xl md:font-semibold"> {title} </h3> {type && ( <span className="h-7 w-fit rounded-md bg-[#FFFFFF1A] py-1 pl-2 pr-1 text-sm text-[#FFA800]"> {type} </span> )} </div> <ul className="mt-4 flex flex-col flex-wrap gap-2 sm:flex-row sm:gap-4"> {(visitors || numberOfSales) && ( <IconText text={(visitors || numberOfSales)?.toString() || ''} icon={Likes} /> )} {siteAge && <IconText text={siteAge} icon={Timer} />} {earned && <IconText text={earned} icon={Earning} />} {(ratings || githubStars) && ( <IconText text={(ratings || githubStars)?.toString() || ''} icon={Star} /> )} </ul> </div> <figure className="flex h-[150px] w-[200px] justify-end overflow-hidden"> <img src={cover} alt="Project Cover" className="h-full w-full rounded-md object-contain" /> </figure> </div> <div className="mb-7 mt-5 rounded-2xl border border-[#444444] bg-[#222222] p-4"> <p className="text-base font-light">{shortDescription}</p> </div> <div className="flex gap-5"> {livePreview && ( <a href={livePreview} className="flex gap-2 text-sm text-[#18F2E5] underline underline-offset-[3px] transition-all duration-75 ease-linear hover:scale-105 md:gap-3 md:text-base" target="_blank" > <img src={Preview} alt="monthly visitors" className="w-[18px] md:w-5" /> <span>Live Preview</span> </a> )} {githubLink && ( <a href={githubLink} className="flex gap-2 text-sm text-[#18F2E5] underline underline-offset-[3px] transition-all duration-75 ease-linear hover:scale-105 md:gap-3 md:text-base" target="_blank" > <img src={GithubDark} alt="monthly visitors" className="w-[18px] md:w-5" /> <span>Github Link</span> </a> )} </div> </div> ) } export default ProjectCard

Points to be noted

  1. I have extracted the icon component into a reusable component named IconText.
  2. I am importing all the SVGs from a common utility file to make updates easier and save some lines of code.
  3. Since the project is built in dark mode, I have added background and text colors in the index.css file for the body.
body { @apply bg-[#222222] text-white; }

Usage: Project Card in Action

You have the flexibility to render and display the project card component as you see fit, whether by stacking them or using a grid layout.

Project cards section in portfolio

I have extracted all the project data and added it to the appData folder to make our code cleaner and address points of concern.

import { projects } from './appData' import ProjectCard from './components/ProjectCard/ProjectCard' function App() { return ( <div className="mx-auto max-w-[1200px] px-3 py-10"> <h2 className="text-3xl font-semibold tracking-wider">Projects</h2> <hr className="mb-8 mt-4 h-px border-0 bg-[#18F2E5]"></hr> <div className="grid grid-cols-1 gap-x-8 gap-y-8 md:grid-cols-2"> {projects.map((project) => ( <ProjectCard data={project} /> ))} </div> </div> ) } export default App

Project data

Here is the project data in case you are interested.

export const projects = [ { priority: 1, title: 'Project Alpha', shortDescription: 'A groundbreaking project that revolutionizes the way we approach technology. Built with cutting-edge tools for maximum efficiency, it sets new industry standards.', cover: 'https://images.unsplash.com/photo-1585282263861-f55e341878f8?q=80&w=2070&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D', livePreview: 'https://example.com/alpha', type: 'Client Work 🙍‍♂️', siteAge: '1 month old', }, { priority: 2, title: 'Project Beta', shortDescription: 'Project Beta is a static technical blog site built with GatsbyJS. I share tips on topics like building reusable components in React, explaining JavaScript methods and concepts, Node.js scripts, and more.', cover: 'https://plus.unsplash.com/premium_photo-1663040328859-48bddaa9dfeb?q=80&w=2070&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D', livePreview: 'https://example.com/beta', visitors: '8K Visitors', earned: '$400 Earned', }, { priority: 3, title: 'Project Epsilon', shortDescription: 'A collection of engaging coding challenges designed to help developers improve their ReactJS skills by writing functional business logic. Your task is to make it functional by writing business logic, to improve your frontend skills', cover: 'https://plus.unsplash.com/premium_photo-1661700152890-931fb04588e6?q=80&w=2070&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D', type: 'Free 🔥', livePreview: 'https://example.com/epsilon', githubLink: 'https://github.com/example/ReactJS-Coding-Challenges', githubStars: '40 Stars', numberOfSales: '138 Sales', }, { priority: 4, title: 'Ejucationzz', shortDescription: 'Ejucationzz is a directory site I created for myself using Next.js. On Ejucationzz, you can find free and paid online and offline courses available in Pakistan. 14 academies and 12 main categories, each with subcategories, have been listed.', cover: 'https://images.unsplash.com/photo-1527334919515-b8dee906a34b?q=80&w=2070&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D', type: 'New 🔥', livePreview: 'https://example.com/Ejucationzz', siteAge: '4 months old', visitors: '100 Visitors', githubLink: '', earned: '', }, ]

If this component helps you in your project and you have any component requests, feel free to contact me on LinkedIn.


Flexy UI Newsletter

Build better and faster UIs.Get the latest components and hooks directly in your inbox. No spam!