The Blog component displays blog posts in a responsive grid with featured images, author information, publish dates, and category tags.
Import
import Blog from "@/components/Blog";
Usage
import Blog from "@/components/Blog";
export default function Home() {
return (
<>
<Blog />
</>
);
}
src/components/Blog/index.tsx
import SectionTitle from "../Common/SectionTitle";
import SingleBlog from "./SingleBlog";
import blogData from "./blogData";
const Blog = () => {
return (
<section id="blog" className="bg-gray-light dark:bg-bg-color-dark py-16 md:py-20 lg:py-28">
<SectionTitle
title="Nuestro ultimo Blogs"
paragraph="El Futuro del Marketing ya Está Aquí..."
center
/>
<div className="grid grid-cols-1 gap-x-8 gap-y-10 md:grid-cols-2 md:gap-x-6 lg:gap-x-8 xl:grid-cols-3">
{blogData.map((blog) => (
<div key={blog.id} className="w-full">
<SingleBlog blog={blog} />
</div>
))}
</div>
</section>
);
};
Data Structure
Blog Type
From src/types/blog.ts:
type Author = {
name: string;
image: string;
designation: string;
};
export type Blog = {
id: number;
title: string;
paragraph: string;
image: string;
author: Author;
tags: string[];
publishDate: string;
};
Unique identifier for the blog post (used as React key)
Post excerpt or summary text (1-2 sentences)
Featured image path (recommended: 740x440px)
Author information object
Author avatar/photo path (recommended: 100x100px)
Array of category tags (first tag displays as badge)
Publication date (e.g., “Dec 22, 2023”)
Component Props
SingleBlog
The SingleBlog component renders individual blog cards:
src/components/Blog/SingleBlog.tsx
const SingleBlog = ({ blog }: { blog: Blog }) => {
const { title, image, paragraph, author, tags, publishDate } = blog;
return (
<div className="group relative overflow-hidden rounded-sm bg-white shadow-one duration-300 hover:shadow-two dark:bg-dark dark:hover:shadow-gray-dark">
<Link href="/blog-details" className="relative block aspect-[37/22] w-full">
<span className="absolute right-6 top-6 z-20 inline-flex items-center justify-center rounded-full bg-primary px-4 py-2 text-sm font-semibold capitalize text-white">
{tags[0]}
</span>
<Image src={image} alt="image" fill />
</Link>
<div className="p-6 sm:p-8 md:px-6 md:py-8 lg:p-8 xl:px-5 xl:py-8 2xl:p-8">
<h3>
<Link href="/blog-details" className="mb-4 block text-xl font-bold text-black hover:text-primary dark:text-white dark:hover:text-primary sm:text-2xl">
{title}
</Link>
</h3>
<p className="mb-6 border-b border-body-color border-opacity-10 pb-6 text-base font-medium text-body-color dark:border-white dark:border-opacity-10">
{paragraph}
</p>
<div className="flex items-center">
{/* Author info */}
<div className="mr-5 flex items-center border-r border-body-color border-opacity-10 pr-5 dark:border-white dark:border-opacity-10">
<div className="relative h-10 w-10 overflow-hidden rounded-full">
<Image src={author.image} alt="author" fill />
</div>
<div className="w-full">
<h4 className="mb-1 text-sm font-medium text-dark dark:text-white">
By {author.name}
</h4>
<p className="text-xs text-body-color">{author.designation}</p>
</div>
</div>
{/* Publish date */}
<div className="inline-block">
<h4 className="mb-1 text-sm font-medium text-dark dark:text-white">
Date
</h4>
<p className="text-xs text-body-color">{publishDate}</p>
</div>
</div>
</div>
</div>
);
};
Blog object containing all post data
Adding New Blog Posts
Prepare images
Add images to /public/images/blog/:
- Featured image: 740x440px (aspect ratio 37:22)
- Author avatar: 100x100px
Add to blogData
Edit src/components/Blog/blogData.tsx:const blogData: Blog[] = [
{
id: 1,
title: "The Future of AI in Marketing",
paragraph: "Discover how artificial intelligence is transforming digital marketing strategies in 2024.",
image: "/images/blog/blog-01.jpg",
author: {
name: "John Doe",
image: "/images/blog/author-01.png",
designation: "Marketing Director",
},
tags: ["AI", "Marketing"],
publishDate: "Dec 22, 2023",
},
// More posts...
];
Update blog detail page
Ensure /app/blog-details/page.tsx exists to handle clicks
Customization
Section Title
src/components/Blog/index.tsx
<SectionTitle
title="Nuestro ultimo Blogs"
paragraph="El Futuro del Marketing ya Está Aquí: Aprovecha las Tendencias que Dejarán Atrás a tus Competidores."
center
/>
Grid Layout
Change the number of columns:
// Default: 1, 2, 3 columns
className="grid grid-cols-1 md:grid-cols-2 xl:grid-cols-3"
// Two columns max
className="grid grid-cols-1 md:grid-cols-2"
// Four columns on large screens
className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4"
Card Spacing
// Default gaps
className="gap-x-8 gap-y-10 md:gap-x-6 lg:gap-x-8"
// Uniform spacing
className="gap-8"
// Tighter spacing
className="gap-4"
Tag Badge
Customize the category tag badge:
src/components/Blog/SingleBlog.tsx
// Default: Primary color
className="bg-primary text-white"
// Custom colors per category
className={`${
tags[0] === "AI" ? "bg-blue-500" :
tags[0] === "Design" ? "bg-purple-500" :
"bg-primary"
} text-white`}
Display all tags instead of just the first:
<div className="absolute right-6 top-6 z-20 flex gap-2">
{tags.map((tag, index) => (
<span key={index} className="rounded-full bg-primary px-3 py-1 text-xs text-white">
{tag}
</span>
))}
</div>
Excerpt Length
Limit paragraph length:
<p className="...">
{paragraph.length > 150 ? `${paragraph.substring(0, 150)}...` : paragraph}
</p>
Image Configuration
Featured Image
The featured image uses Next.js Image with fill:
<Link href="/blog-details" className="relative block aspect-[37/22] w-full">
<Image src={image} alt="image" fill />
</Link>
Recommended specs:
- Aspect ratio: 37:22 (or 1.68:1)
- Dimensions: 740x440px minimum
- Format: WebP or JPG
- Optimization: Use Next.js automatic optimization
Author Avatar
<div className="relative h-10 w-10 overflow-hidden rounded-full">
<Image src={author.image} alt="author" fill />
</div>
Styling
Dark Mode
Blog cards automatically adapt:
className="bg-white dark:bg-dark"
className="text-black dark:text-white"
className="shadow-one hover:shadow-two dark:hover:shadow-gray-dark"
Background Color
The section has a light gray background:
className="bg-gray-light dark:bg-bg-color-dark"
Hover Effects
Cards and links have hover transitions:
// Card shadow
className="shadow-one duration-300 hover:shadow-two"
// Title color
className="hover:text-primary dark:hover:text-primary"
Linking to Blog Posts
Static Links
Currently links to a single page:
<Link href="/blog-details">
Dynamic Links
Make links dynamic based on blog ID or slug:
// Add slug to Blog type
type Blog = {
// ... existing fields
slug: string;
};
// Use dynamic link
<Link href={`/blog/${blog.slug}`}>
Accessibility
- Section has
id="blog" for anchor navigation
- Proper heading hierarchy (h3 for post titles)
- Alt text for all images
- Semantic HTML structure
- Keyboard-navigable links
- Sufficient color contrast
Best Practices
Optimize images - Use Next.js Image optimization and serve WebP format for better performance.
Consistent posting - Keep a regular publishing schedule. Show only your latest 3-6 posts on the homepage.
SEO-friendly titles - Write descriptive titles (50-60 characters) that include target keywords.
Engaging excerpts - Write compelling summaries that make readers want to click through.
Advanced Features
Read Time Estimate
Add reading time:
type Blog = {
// ... existing fields
readTime: string; // e.g., "5 min read"
};
// Display it
<p className="text-xs text-body-color">{blog.readTime}</p>
View Count
Show post popularity:
<div className="flex items-center gap-2">
<EyeIcon className="h-4 w-4" />
<span className="text-xs">{viewCount} views</span>
</div>
Featured Posts
Highlight important posts:
type Blog = {
// ... existing fields
featured?: boolean;
};
// Show featured posts first
const sortedBlogs = [...blogData].sort((a, b) =>
(b.featured ? 1 : 0) - (a.featured ? 1 : 0)
);
For many posts:
const postsPerPage = 6;
const displayedPosts = blogData.slice(0, postsPerPage);
<Link href="/blog" className="mt-8 text-center">
View All Posts →
</Link>