4 Tailwind Blog Card Components for Your React and Next.js App
Marketing evolves over time. If your business is online, you need some form of marketing.
Blogging is considered a key part of this strategy.
Despite recent updates from Google and advancements in large language model software, blogging remains one of the best sources of marketing.
Today, I will share a responsive blog card component UI with 4 variants built with React and Tailwind CSS. To use these UI components in your project, all you need to do is copy and paste.
Blog Section UI | Vertical Card
This is what our blog card section looks like:
Blog Card Component Code | Variant 1
If you are only interested in the blog card component, you can use the code below.
1import { FC } from 'react' 2 3interface BlogPost { 4 title: string 5 shortDescription: string 6 cover: string 7 slug: string 8 publishDate: string 9 estimatedTimeToRead: string 10} 11 12interface BlogCardProps { 13 post: BlogPost 14} 15 16const BlogCard: FC<BlogCardProps> = ({ post }) => { 17 const { 18 title, 19 shortDescription, 20 cover, 21 slug, 22 publishDate, 23 estimatedTimeToRead, 24 } = post 25 26 return ( 27 <div 28 key={slug} 29 className="flex transform flex-col gap-3 rounded-lg border bg-[#F4F4F5] p-3 transition-transform hover:scale-105" 30 > 31 <figure className="relative h-40 w-full overflow-hidden bg-gray-200"> 32 <img 33 className="absolute inset-0 h-full w-full rounded-md object-cover" 34 src={cover} 35 alt="demo" 36 /> 37 </figure> 38 39 <a href={`/${slug}`}> 40 <h3 className="mb-2 text-xl font-bold text-gray-600 transition-colors duration-200 hover:text-blue-600"> 41 {title} 42 </h3> 43 <p className="text-gray-700">{shortDescription}</p> 44 <p className="mt-4 text-sm font-semibold text-gray-600"> 45 {formatDate(publishDate)} | {estimatedTimeToRead} 46 </p> 47 </a> 48 </div> 49 ) 50} 51 52export default BlogCard
Horizontal Blog Card Section | Variant 2
To make our landing/home page look elegant, I built another variant of blog component that is a bit different than the first one / Vertical card.
In this Blog Card component, we show images horizontally with card data. We are also scaling the blog card image on hover.
Horizontal Blog Card UI
This is the desktop view of Horizontal Blog Card.
Horizontal Blog Card Code | Variant 2
Types and props are exactly same as previous blog card component, only the UI is different.
1import { FC } from 'react' 2import { formatDate } from '../utils' 3 4interface BlogPost { 5 title: string 6 shortDescription: string 7 cover: string 8 slug: string 9 publishDate: string 10 estimatedTimeToRead: string 11} 12 13interface BlogCardProps { 14 post: BlogPost 15} 16 17const BlogCard: FC<BlogCardProps> = ({ post }) => { 18 const { 19 title, 20 shortDescription, 21 cover, 22 slug, 23 publishDate, 24 estimatedTimeToRead, 25 } = post 26 27 return ( 28 <div 29 key={slug} 30 className="flex flex-col gap-3 rounded-lg border bg-[#F4F4F5] p-3 lg:flex-row" 31 > 32 <figure className="relative mt-1 h-24 min-w-40 overflow-hidden bg-gray-200"> 33 <img 34 className="absolute inset-0 h-full w-full rounded-md object-cover transition-transform duration-300 hover:scale-125" 35 src={cover} 36 alt="demo" 37 /> 38 </figure> 39 40 <a href={`/${slug}`}> 41 <h3 className="mb-2 text-xl font-bold text-gray-600 transition-colors duration-200 hover:text-blue-600"> 42 {title} 43 </h3> 44 <p className="text-gray-700">{shortDescription}</p> 45 <p className="mt-4 text-sm font-semibold text-gray-600"> 46 {formatDate(publishDate)} | {estimatedTimeToRead} 47 </p> 48 </a> 49 </div> 50 ) 51} 52 53export default BlogCard
To format the date in the desired format, I have built a utility function formatDate
.
That convert '2024-07-14' into 14th July 2024. Use it if you need it.
1function formatDate(dateString: string): string { 2 const date = new Date(dateString) 3 4 const day = date.getDate() 5 const month = date.toLocaleString('default', { month: 'long' }) 6 const year = date.getFullYear() 7 8 const daySuffix = getDaySuffix(day) 9 10 return `${day}${daySuffix} ${month} ${year}` 11} 12 13function getDaySuffix(day: number): string { 14 if (day > 3 && day < 21) return 'th' 15 switch (day % 10) { 16 case 1: 17 return 'st' 18 case 2: 19 return 'nd' 20 case 3: 21 return 'rd' 22 default: 23 return 'th' 24 } 25}
Best Practices for a Blog App
Typically, we don't have just one blog card in our app. We usually have a section that displays multiple blogs.
To render multiple blogs, I've built a BlogList
component that renders a list of BlogCard
components to make it easier to use throughout our app.
Blog Section Component
The BlogList
|| BlogSection
component is built using TypeScript and accepts two props
prop: posts
, which is an array of blogs.type
, that is either horizontal or vertical
In return, we render blog card components on the basis of the provided type.
1import { FC } from 'react' 2import VerticalBlogCard from './VerticalBlogCard' 3import HorizontalBlogCard from './HorizontalBlogCard' 4 5interface BlogPost { 6 title: string 7 shortDescription: string 8 cover: string 9 slug: string 10 publishDate: string 11 estimatedTimeToRead: string 12} 13 14interface BlogListProps { 15 posts: BlogPost[] 16 type?: 'vertical' | 'horizontal' 17} 18 19const BlogList: FC<BlogListProps> = ({ posts, type = 'vertical' }) => { 20 const isVertical = type === 'vertical' 21 22 if (isVertical) { 23 return ( 24 <div className="grid gap-6 sm:grid-cols-2 md:grid-cols-3"> 25 {posts.map((post) => ( 26 <VerticalBlogCard key={post.slug} post={post} /> 27 ))} 28 </div> 29 ) 30 } else { 31 return ( 32 <div className="grid gap-6 sm:grid-cols-2"> 33 {posts.map((post) => ( 34 <HorizontalBlogCard key={post.slug} post={post} /> 35 ))} 36 </div> 37 ) 38 } 39} 40 41export default BlogList
How to Use It
Here's how we can use the BlogSection component by passing an array of posts, and type of blog card variants.
1import BlogList from './components/BlogList' 2 3const posts = [ 4 { 5 title: 'React Tailwind Newsletter Component', 6 shortDescription: 7 'Learn how to build a responsive newsletter component using React and Tailwind CSS.', 8 cover: 9 'https://plus.unsplash.com/premium_photo-1673984261110-d1d931e062c0?q=80&w=2070&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D', 10 slug: 'react-tailwind-newsletter-component', 11 publishDate: '2024-07-14', 12 estimatedTimeToRead: '5 min read', 13 }, 14 { 15 title: 'Advanced JavaScript Concepts', 16 shortDescription: 17 'Deep dive into closures, prototypes, and asynchronous programming in JavaScript.', 18 cover: 19 'https://images.unsplash.com/photo-1493612276216-ee3925520721?q=80&w=1964&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D', 20 slug: 'advanced-javascript-concepts', 21 publishDate: '2024-05-25', 22 estimatedTimeToRead: '10 min read', 23 }, 24 { 25 title: 'Introduction to TypeScript', 26 shortDescription: 27 'An introductory guide to TypeScript, covering the basics and helping you get started with typed JavaScript.', 28 cover: 29 'https://plus.unsplash.com/premium_photo-1663100722417-6e36673fe0ed?q=80&w=2070&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D', 30 slug: 'introduction-to-typescript', 31 publishDate: '2024-02-10', 32 estimatedTimeToRead: '7 min read', 33 }, 34 { 35 title: 'Building a REST API with Node.js', 36 shortDescription: 37 'Step-by-step guide to creating a RESTful API using Node.js and Express.', 38 cover: 39 'https://images.unsplash.com/photo-1560732488-6b0df240254a?q=80&w=2070&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D', 40 slug: 'building-a-rest-api-with-node-js', 41 publishDate: '2024-04-15', 42 estimatedTimeToRead: '12 min read', 43 }, 44 { 45 title: 'CSS Grid Layout: A Complete Guide', 46 shortDescription: 47 'Master CSS Grid Layout with this comprehensive guide covering all the essentials and advanced techniques.', 48 cover: 49 'https://images.unsplash.com/photo-1501594907352-04cda38ebc29?q=80&w=2070&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D', 50 slug: 'css-grid-layout-complete-guide', 51 publishDate: '2024-03-20', 52 estimatedTimeToRead: '15 min read', 53 }, 54 { 55 title: 'Getting Started with Next.js', 56 shortDescription: 57 'A beginner’s guide to building server-rendered React applications with Next.js.', 58 cover: 59 'https://plus.unsplash.com/premium_photo-1680404114169-e254afa55a16?q=80&w=1964&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D', 60 slug: 'getting-started-with-next-js', 61 publishDate: '2024-06-10', 62 estimatedTimeToRead: '8 min read', 63 }, 64] 65 66function App() { 67 return ( 68 <> 69 <Navbar /> 70 <Header title={title} description={description} /> 71 72 <div className="mx-auto max-w-6xl px-3"> 73 <CategoryList categories={categories} /> 74 75 <SectionHeading 76 title="Popular Articles" 77 subtitle="Diverse Range of articles related to html css and javascript" 78 /> 79 <BlogList posts={posts} /> 80 81 <SectionHeading 82 title="Latest Articles" 83 subtitle="Diverse Range of articles related to html css and javascript" 84 /> 85 <BlogList posts={posts} type="horizontal" /> 86 87 <Faq items={Faqs} /> 88 <Newsletter /> 89 </div> 90 <Footer /> 91 </> 92 ) 93} 94 95export default App
Blog Card Component For Tech or Coding Blog
The Tech Blog Card Component is built for tech or coding blogs.
I keep the design minimal. It highlights following key details:
- Title
- Category
- Description
- Tags
- Metadata (date, reading time)
It's perfect for modern tech blogs.
Tech Blog Card UI
Tech Blog Card Component Code
1import { Atom, BookCopy, Code2 } from 'lucide-react' 2import { FC, ReactNode } from 'react' 3 4interface ArticleCardProps { 5 post: { 6 title: string 7 shortDescription: string 8 slug: string 9 tags: string[] 10 category: string 11 date: string 12 readingTime: string 13 } 14} 15 16interface Category { 17 title: string 18 icon: ReactNode 19} 20 21const categories: Record<string, Category> = { 22 React: { title: 'React', icon: <Atom /> }, 23 JavaScript: { title: 'JavaScript', icon: <Code2 /> }, 24 CSS: { title: 'CSS', icon: <BookCopy /> }, 25} 26 27const ArticleCard: FC<ArticleCardProps> = ({ post }) => { 28 const { slug, title, shortDescription, tags, date, readingTime, category } = 29 post 30 31 return ( 32 <div className="rounded-[10px] border border-green-200 bg-white p-5 shadow-[0px_8px_8px_0px_#00000012] transition-shadow duration-300 ease-out hover:shadow-[0px_10px_10px_rgba(181,181,181,0.4)]"> 33 <a href={slug} className="no-underline"> 34 <div className="topic-collection-icon-wrapper h-[60px] w-[60px] cursor-pointer rounded-[18px] border border-[rgba(16,49,89,0.1)] sm:h-[50px] sm:w-[50px]"> 35 <div className="topic-collection-svg flex h-full w-full items-center justify-center"> 36 {categories[category].icon} 37 </div> 38 </div> 39 <h3 className="mb-4 mt-2 line-clamp-3 text-[20px] font-semibold leading-[1.3] text-[#202020]"> 40 {title} 41 </h3> 42 <p className="mb-3 line-clamp-3 overflow-hidden text-ellipsis leading-[1.4] text-[#7d7d7d]"> 43 {shortDescription} 44 </p> 45 <div className="mb-3 mt-6 flex flex-wrap gap-2"> 46 {tags.map((tag) => ( 47 <div 48 key={tag.trim()} 49 className="inline-block rounded-md bg-[#e3f2ff] px-3 py-[5px] capitalize" 50 > 51 <span className="text-xs font-normal text-[#0b5599]">{tag}</span> 52 </div> 53 ))} 54 </div> 55 <div className="mt-2 flex items-center"> 56 <time className="text-sm font-normal text-[#919191]" dateTime={date}> 57 {date} 58 </time> 59 <span className="relative ml-5 text-sm font-normal text-[#919191] before:absolute before:left-[-11px] before:top-1/2 before:h-[2px] before:w-[2px] before:rounded-full before:bg-[#909090] before:content-['']"> 60 {readingTime} 61 </span> 62 </div> 63 </a> 64 </div> 65 ) 66} 67 68export default ArticleCard
Rendering Category Icons Conditionally
The Tech Blog Card Component renders category icons dynamically based on the article's category.
- The categories object defines a mapping between the category and its associated icon.
- When rendering an article card, the component checks the category of the article and displays the corresponding icon using
{categories[category].icon}
. - For example, if the article's category is React, the Atom icon is displayed; for JavaScript, the Code2 icon is displayed; and for CSS, the BookCopy icon is used.
How to use ArticleCard Component
Article Card Data
1const articleData = [ 2 { 3 title: 'Mastering React Hooks: A Comprehensive Guide', 4 shortDescription: 5 'Explore React hooks and how to leverage them for building efficient functional components', 6 slug: 'https://yourblog.com/article/react-hooks', 7 tags: ['React', 'Hooks', 'Frontend'], 8 category: 'React', 9 date: '2024-08-15', 10 readingTime: '7 min read', 11 }, 12 // ... article 2 data 13 // ... article 3 dat 14]
Rendering in App.tsx
1function App() { 2 return ( 3 <main> 4 <Navbar /> 5 <Header title={title} description={description} /> 6 <div className="mx-auto max-w-6xl px-3"> 7 <SectionHeading 8 title="Latest Articles" 9 subtitle="Shop by Category" 10 shortDescription="Read our latest expertly crafted articles" 11 /> 12 13 <div className="mb-12 grid gap-6 sm:grid-cols-2 md:grid-cols-3"> 14 {articleData.map((post) => ( 15 <ArticleCard post={post} /> 16 ))} 17 </div> 18 19 <Faq items={Faqs} /> 20 </div> 21 <Newsletter /> 22 23 <Footer /> 24 </main> 25 ) 26}
Clean and Minimal Blog Card Component
This clean blog card design is perfect for displaying blog posts in a responsive grid. The component is lightweight, customizable, and follows modern UI practices using Tailwind CSS.
Clean Blog Card UI
How To Use Clean Blog Card Component In Your Project?
Here’s a breakdown of how to use this component and integrate it into your project:
Step 1: Clean Blog Card Component Code
The BlogCard
component takes a post
object as a prop and dynamically displays the title, image, category, and published date. This structure allows easy reuse across your project.
1import { FC } from 'react' 2import { formatDate } from '../../utils' 3 4interface BlogPost { 5 title: string 6 shortDescription: string 7 cover: string 8 slug: string 9 category: string 10 publishDate: string 11} 12 13interface BlogCardProps { 14 post: BlogPost 15} 16 17const BlogCard: FC<BlogCardProps> = ({ post }) => { 18 const { title, slug, cover, publishDate, category } = post 19 20 return ( 21 <a href={`blogs/${slug}`} className="no-underline"> 22 <div 23 key={slug} 24 className="flex transform flex-col gap-3 transition-transform hover:scale-105" 25 > 26 <figure className="relative h-48 w-full overflow-hidden"> 27 <img 28 src={cover} 29 alt={title} 30 className="h-full w-full rounded-xl bg-gray-200 object-cover" 31 /> 32 </figure> 33 34 <div className="mt-1 flex items-center gap-2"> 35 <span className="w-fit rounded-xl bg-violet-100 px-3 py-1 text-sm font-bold text-violet-700"> 36 {category} 37 </span> 38 <p className="text-sm font-semibold text-gray-500"> 39 {formatDate(publishDate)} 40 </p> 41 </div> 42 43 <h3 className="hover:text-theme mb-2 text-xl font-bold transition-colors duration-200"> 44 {title} 45 </h3> 46 </div> 47 </a> 48 ) 49} 50 51export default BlogCard
Step 2: Define the Blog Data
Create a posts
array with blog details. Each object should include title
, shortDescription
, cover
, slug
, category
, and publishDate
.
1export const posts = [ 2 { 3 title: 'React Tailwind Newsletter Component', 4 shortDescription: 5 'Learn how to build a responsive newsletter component using React and Tailwind CSS.', 6 cover: 7 'https://plus.unsplash.com/premium_photo-1673984261110-d1d931e062c0?q=80&w=2070&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D', 8 slug: 'react-tailwind-newsletter-component', 9 category: 'React', 10 publishDate: '2024-07-14', 11 estimatedTimeToRead: '5 min read', 12 }, 13 { 14 // post 2 15 }, 16 { 17 // post 3 18 }, 19]
Step 3: Render the BlogCard in the App
Component
Use the posts
data and map over it to render the BlogCard
for each blog post.
1import BlogCard from './components/BlogCard/Card4' 2import { posts } from './data' 3 4const App = () => { 5 return ( 6 <div className="mx-auto max-w-7xl p-5"> 7 <h1 className="mb-10 text-center text-2xl font-bold"> 8 Latest Blog Posts 9 </h1> 10 <div className="grid gap-x-14 gap-y-12 sm:grid-cols-1 md:grid-cols-2 lg:grid-cols-3"> 11 {posts.map((blog) => ( 12 <BlogCard key={blog.slug} post={blog} /> 13 ))} 14 </div> 15 </div> 16 ) 17} 18 19export default App
Notes:
posts
Data: Ensure your blog data follows the structure outlined above.formatDate
Utility: This utility function formats thepublishDate
to a readable format.- Customizable UI: Modify the Tailwind classes for a personalized design.
You can also find the React Tailwind Section Heading Component
, which you have seen in the UI and code above.
Feel free to find me on LinkedIn if you have any component requests or suggestions.