Blog Grid

Blog post grid with featured card and smaller cards layout. Includes category filters and separate mobile tile layout. Data sourced from Sanity CMS.

Live Preview

Showing with mock data. Images would come from Sanity CMS.

View Full Version

Props

posts

BlogPost[] (required)

Array of blog post data from Sanity CMS.

title

string (default: "Latest articles")

Section heading. Set to empty string to hide.

subtitle

string (optional)

Description text below heading.

showViewAll

boolean (default: false)

Whether to show "View all posts" button.

maxCols

2 | 3 (default: 3)

Maximum columns on desktop.

noPadding

boolean (default: false)

Remove section padding for embedding in other layouts.

Desktop Layout

Featured Card

First post displayed larger with horizontal layout. Uses .card-featured class with gradient background.

Grid Cards (2-3 columns)

Remaining posts in responsive grid. Uses .card-blog class for consistent styling.

Category Filters

Badge filter pills using filter/filterActive variants

Mobile Layout

Tile Layout

Compact horizontal tiles with thumbnail and text. Uses the shared .card-tile class.

Swipeable Filters

Horizontal scroll for category pills on mobile

Card Styling

.card-blog

Gradient from accent-6 to surface-deep

.card-blog:hover

Intensified gradient with accent border

.card-featured

Larger gradient for hero-style display

.card-tile

Shared mobile compact tile (also used by case-study tiles)

Data from Sanity

tsx
// Query for blog posts
const query = `
  *[_type == "blogPost"] | order(publishedAt desc) {
    _id,
    title,
    slug,
    publishedAt,
    excerpt,
    mainImage,
    tags
  }
`

interface BlogPost {
  _id: string
  title: string
  slug: { current: string }
  publishedAt: string
  excerpt?: string
  mainImage?: SanityImage
  tags?: string[]
}

Category Filter Component

tsx
import { CategoryFilters } from '@/components/ui/CategoryFilters'

// Gets unique categories from posts
<CategoryFilters
  categories={['All', 'Design', 'Development', 'Strategy']}
  activeCategory={activeCategory}
  onCategoryChange={setActiveCategory}
/>

Usage

tsx
import { BlogGrid } from '@/components/sections/BlogGrid'
import { CategoryFilters } from '@/components/ui/CategoryFilters'

export default async function BlogPage() {
  const posts = await getBlogPosts()
  return (
    <main>
      <CategoryFilters {...filterProps} />
      <BlogGrid posts={posts} />
    </main>
  )
}

// With custom options
<BlogGrid
  posts={posts}
  showViewAll={true}
  maxCols={2}
  title="Related Articles"
/>

Import

tsx
import { BlogGrid } from '@/components/sections/BlogGrid'
import { FeaturedBlogCard } from '@/components/sections/FeaturedBlogCard'
import { CategoryFilters } from '@/components/ui/CategoryFilters'

Related Files

src/components/sections/BlogGrid.tsxsrc/components/sections/FeaturedBlogCard.tsxsrc/components/sections/CategoryFilters.tsxsanity/schemas/blogPost.ts