Skip to main content
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

src/app/page.tsx
import Blog from "@/components/Blog";

export default function Home() {
  return (
    <>
      <Blog />
    </>
  );
}

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;
};
id
number
required
Unique identifier for the blog post (used as React key)
title
string
required
Blog post title/headline
paragraph
string
required
Post excerpt or summary text (1-2 sentences)
image
string
required
Featured image path (recommended: 740x440px)
author
Author
required
Author information object
author.name
string
required
Author’s full name
author.image
string
required
Author avatar/photo path (recommended: 100x100px)
author.designation
string
required
Author’s title or role
tags
string[]
required
Array of category tags (first tag displays as badge)
publishDate
string
required
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
Blog
required
Blog object containing all post data

Adding New Blog Posts

1

Prepare images

Add images to /public/images/blog/:
  • Featured image: 740x440px (aspect ratio 37:22)
  • Author avatar: 100x100px
2

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...
];
3

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`}

Show Multiple Tags

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

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

Currently links to a single page:
<Link href="/blog-details">
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>
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)
);

Pagination

For many posts:
const postsPerPage = 6;
const displayedPosts = blogData.slice(0, postsPerPage);

<Link href="/blog" className="mt-8 text-center">
  View All Posts →
</Link>

Build docs developers (and LLMs) love