# Blog component 15

> Tabbed blog grid with centered header, scroll area tabs with category filtering (All, Product, Design, Startup Growth), search input with icon, three-column responsive grid of blog cards featuring hover-scale images, calendar icon with dates, primary/10 category badges, line-clamp titles and descriptions, author names, outlined arrow icon buttons with primary hover states

A shadcn/ui block from the [@shadcn-studio](https://shadcnstudio.com) registry, listed on [shadcn.exchange](https://shadcn.exchange/r/shadcn-studio/blog-component-15).

- Page, with a live preview: https://shadcn.exchange/r/shadcn-studio/blog-component-15
- Screenshot: https://abzjhwgwpdpz7ua5.public.blob.vercel-storage.com/shots/shadcn-studio/blog-component-15-light-2561de102c3b72e9.png
- Registry item: https://shadcnstudio.com/r/new-york-v4/blog-component-15.json
- Listed: 2026-10-13, source last changed 2026-10-13

## Install

```bash
npx shadcn@latest add https://shadcnstudio.com/r/new-york-v4/blog-component-15.json
```

Run it in a project that has shadcn/ui set up. It adds the files below and installs what they depend on.

## Dependencies

- npm packages: `lucide-react`
- Registry items: `badge`, `button`, `card`, `input`, `input-group`, `scroll-area`, `tabs`

## Files

### `src/registry/new-york/blocks/marketing-ui/blog-component/blog-component-15/page.tsx`

```tsx
import Blog from '@/registry/new-york/components/blocks/blog-component-15/blog-component-15'

const blogPosts = [
  {
    title: 'Design Smarter: How User Behavior Shapes Winning Products',
    description: 'Learn how to discover what users truly want and build with confidence.',
    imageUrl: 'https://cdn.shadcnstudio.com/ss-assets/template/landing-page/ink/image-04.png',
    imageAlt: 'Design workspace with color swatches',
    date: 'March 12, 2025',
    category: 'Product',
    author: 'Phillip Palmer',
    authorLink: '#',
    blogLink: '#',
    categoryLink: '#'
  },
  {
    title: 'Nail Your First Launch: A Checklist for Product Debut Success',
    description: 'Avoid common launch traps and create excitement from day one.',
    imageUrl: 'https://cdn.shadcnstudio.com/ss-assets/template/landing-page/ink/image-05.png',
    imageAlt: 'Product launch analytics',
    date: 'January 20, 2025',
    category: 'Startup Growth',
    author: 'Michael Brown',
    authorLink: '#',
    blogLink: '#',
    categoryLink: '#'
  },
  {
    title: 'Why Fast Apps Win: The Blueprint for Lightning-Quick Experiences',
    description: 'Explore proven strategies to boost speed and delight users every time.',
    imageUrl: 'https://cdn.shadcnstudio.com/ss-assets/template/landing-page/ink/image-06.png',
    imageAlt: 'Mobile app development',
    date: 'February 28, 2025',
    category: 'Product',
    author: 'Jane Smith',
    authorLink: '#',
    blogLink: '#',
    categoryLink: '#'
  },
  {
    title: 'Scaling Design the Right Way with a Solid Component System',
    description: 'Build consistency, save time, and ship optimized UI every release.',
    imageUrl: 'https://cdn.shadcnstudio.com/ss-assets/template/landing-page/ink/image-07.png',
    imageAlt: 'Component design system',
    date: 'March 05, 2025',
    category: 'Design',
    author: 'Dylan Field',
    authorLink: '#',
    blogLink: '#',
    categoryLink: '#'
  },
  {
    title: 'Product KPIs That Actually Matter And How to Track Them',
    description: 'Measure progress the right way to build momentum and stay focused.',
    imageUrl: 'https://cdn.shadcnstudio.com/ss-assets/template/landing-page/ink/image-08.png',
    imageAlt: 'Team analyzing KPIs',
    date: 'January 09, 2025',
    category: 'Design',
    author: 'Nina Rich',
    authorLink: '#',
    blogLink: '#',
    categoryLink: '#'
  },
  {
    title: 'How AI-Driven Workflows Are Transforming Product Development',
    description: 'Discover smarter ways to ideate, design, and build using AI tools.',
    imageUrl: 'https://cdn.shadcnstudio.com/ss-assets/template/landing-page/ink/image-09.png',
    imageAlt: 'AI in product development',
    date: 'March 05, 2025',
    category: 'Startup Growth',
    author: 'Startup Growth',
    authorLink: '#',
    blogLink: '#',
    categoryLink: '#'
  }
]

const BlogSection = () => {
  return <Blog blogPosts={blogPosts} />
}

export default BlogSection
```

### `src/registry/new-york/components/blocks/blog-component-15/blog-component-15.tsx`

```tsx
import { SearchIcon, ArrowRightIcon, CalendarDaysIcon } from 'lucide-react'

import { Badge } from '@/registry/new-york/ui/badge'
import { Button } from '@/registry/new-york/ui/button'
import { Card, CardContent } from '@/registry/new-york/ui/card'
import { InputGroup, InputGroupAddon, InputGroupInput } from '@/registry/new-york/ui/input-group'
import { ScrollArea, ScrollBar } from '@/registry/new-york/ui/scroll-area'
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/registry/new-york/ui/tabs'

export type BlogPost = {
  title: string
  description: string
  imageUrl: string
  imageAlt: string
  date: string
  category: string
  author: string
  authorLink: string
  blogLink: string
  categoryLink: string
}

type BlogProps = {
  blogPosts: BlogPost[]
}

const BlogGrid = ({ posts }: { posts: BlogPost[] }) => {
  return (
    <div className='grid gap-6 sm:grid-cols-2 lg:grid-cols-3'>
      {posts.map((post, index) => (
        <Card key={index} className='group h-full overflow-hidden shadow-none transition-all duration-300'>
          <CardContent className='space-y-3.5'>
            <div className='mb-6 overflow-hidden rounded-lg sm:mb-12'>
              <a href={post.blogLink}>
                <img
                  src={post.imageUrl}
                  alt={post.imageAlt}
                  className='h-59.5 w-full object-cover transition-transform duration-300 group-hover:scale-105'
                />
              </a>
            </div>
            <div className='flex items-center justify-between gap-1.5'>
              <div className='text-muted-foreground flex items-center gap-1.5'>
                <CalendarDaysIcon className='size-6' />
                <span>{post.date}</span>
              </div>
              <a href={post.categoryLink}>
                <Badge className='bg-primary/10 text-primary rounded-full border-0 text-sm'>{post.category}</Badge>
              </a>
            </div>
            <h3 className='line-clamp-2 text-lg font-medium md:text-xl'>
              <a href={post.blogLink}>{post.title}</a>
            </h3>
            <p className='text-muted-foreground line-clamp-2'>{post.description}</p>
            <div className='flex items-center justify-between'>
              <a href={post.authorLink} className='text-sm font-medium'>
                {post.author}
              </a>
              <Button
                size='icon'
                variant='outline'
                className='group-hover:bg-primary! group-hover:text-primary-foreground group-hover:border-primary hover:border-primary hover:bg-primary! hover:text-primary-foreground'
                asChild
              >
                <a href={post.blogLink}>
                  <ArrowRightIcon className='size-4 -rotate-45' />
                  <span className='sr-only'>Read more: {post.title}</span>
                </a>
              </Button>
            </div>
          </CardContent>
        </Card>
      ))}
    </div>
  )
}

const Blog = ({ blogPosts }: BlogProps) => {
  const categories = ['All', 'Product', 'Design', 'Startup Growth']

  return (
    <section className='py-8 sm:py-16 lg:py-24'>
      <div className='mx-auto max-w-7xl space-y-8 px-4 sm:px-6 lg:space-y-16 lg:px-8'>
        {/* Header */}
        <div className='space-y-4'>
          <p className='text-sm'>Blogs</p>

          <h2 className='text-2xl font-semibold md:text-3xl lg:text-4xl'>
            Build Better Products with Insights & Inspiration.
          </h2>

          <p className='text-muted-foreground text-lg md:text-xl'>
            Practical insights and real stories to guide your product from vision to reality.
          </p>
        </div>

        {/* Tabs and Search */}
        <Tabs defaultValue='All' className='gap-8 lg:gap-16'>
          <div className='flex flex-col items-start justify-between gap-4 sm:flex-row sm:items-center'>
            <ScrollArea className='bg-muted w-full rounded-lg sm:w-auto'>
              <TabsList className='gap-1 group-data-[orientation=horizontal]/tabs:h-auto'>
                {categories.map(category => (
                  <TabsTrigger
                    key={category}
                    value={category}
                    className='hover:bg-primary/10 cursor-pointer rounded-lg px-4 text-base group-data-[orientation=horizontal]/tabs:after:h-0'
                  >
                    {category}
                  </TabsTrigger>
                ))}
              </TabsList>
              <ScrollBar orientation='horizontal' />
            </ScrollArea>

            <InputGroup className='h-10 w-auto max-md:w-full'>
              <InputGroupAddon>
                <SearchIcon className='size-4' />
                <span className='sr-only'>Search</span>
              </InputGroupAddon>
              <InputGroupInput
                type='search'
                placeholder='Search'
                className='peer px-9 [&::-webkit-search-cancel-button]:appearance-none [&::-webkit-search-decoration]:appearance-none [&::-webkit-search-results-button]:appearance-none [&::-webkit-search-results-decoration]:appearance-none'
              />
            </InputGroup>
          </div>

          {/* All Posts Tab */}
          <TabsContent value='All'>
            <BlogGrid posts={blogPosts} />
          </TabsContent>

          {/* Category-specific Tabs */}
          {categories.slice(1).map((category, index) => (
            <TabsContent key={index} value={category}>
              <BlogGrid posts={blogPosts.filter(post => post.category === category)} />
            </TabsContent>
          ))}
        </Tabs>
      </div>
    </section>
  )
}

export default Blog
```
