# Gallery Section v2

> A 3×3 image grid with grayscale-to-color hover transitions, uniform aspect ratios, and mono font captions below each card.

A shadcn/ui block from the [@uiception](https://uiception.com/?utm_source=shadcn.exchange) registry, listed on [shadcn.exchange](https://shadcn.exchange/r/uiception/gallery-section-v2).

- Page, with a live preview: https://shadcn.exchange/r/uiception/gallery-section-v2
- Screenshot: https://shadcn.exchange/shot/uiception/gallery-section-v2/light.png
- Registry item: https://uiception.com/r/gallery-section-v2.json
- Listed: 2026-10-13, source last changed 2026-10-13

## Install

```bash
npx shadcn@latest add https://shadcn.exchange/r/uiception/gallery-section-v2.json
```

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

## Dependencies

- Registry items: `https://uiception.com/r/block-media.json`

## Files

### `registry/new-york/blocks/gallery-section-v2/page.tsx`

```tsx
import GallerySectionV2 from "./components/gallery-section-v2"

export default function Page() {
  return <GallerySectionV2 />
}
```

### `registry/new-york/blocks/gallery-section-v2/components/gallery-section-v2.tsx`

```tsx
import { GalleryCard } from "./gallery-card"
import { createBlockImage } from "@/lib/block-media"
import { galleryFiles, type GalleryItem } from "../lib/config"

const blockImage = createBlockImage("gallery-section-v2")
const galleryItems: GalleryItem[] = galleryFiles.map(
  ({ file, ...rest }, index) => ({
    id: String(index + 1),
    ...rest,
    imageSrc: blockImage(file),
  }),
)

export default function GallerySectionV2() {
  return (
    <section className="py-4 md:py-6 lg:py-8">
      <div className="mx-auto max-w-7xl px-4">
        <h2 className="mb-6 font-mono text-4xl lg:text-5xl">
          Gallery
        </h2>
        <div className="grid grid-cols-3 gap-x-6 gap-y-10">
          {galleryItems.map((item, index) => (
            <GalleryCard
              key={item.id}
              item={item}
              priority={index < 3}
            />
          ))}
        </div>
      </div>
    </section>
  )
}
```

### `registry/new-york/blocks/gallery-section-v2/components/gallery-card.tsx`

```tsx
"use client"

import { useState } from "react"
import Image from "next/image"

import type { GalleryItem } from "../lib/config"

type GalleryCardProps = { item: GalleryItem; priority?: boolean }

export function GalleryCard({ item, priority = false }: GalleryCardProps) {
  const [loaded, setLoaded] = useState(false)

  return (
    <div className="group">
      <div className="relative isolate aspect-4/3 overflow-hidden">
        <Image
          src={item.imageSrc}
          alt={item.alt}
          fill
          unoptimized
          priority={priority}
          sizes="(max-width: 768px) 50vw, 33vw"
          className={`object-cover transition-opacity duration-500${loaded ? " opacity-100" : " opacity-0"}`}
          onLoad={() => setLoaded(true)}
          onError={() => setLoaded(true)}
        />
        {loaded && (
          <div
            aria-hidden
            className="pointer-events-none absolute inset-0 bg-[#808080] mix-blend-color transition-opacity duration-500 group-hover:opacity-0"
          />
        )}
      </div>
      <div className="mt-3 font-mono">
        <p className="text-sm font-medium leading-tight">{item.title}</p>
        <p className="mt-1 text-xs text-muted-foreground">{item.description}</p>
      </div>
    </div>
  )
}
```

### `registry/new-york/blocks/gallery-section-v2/lib/config.ts`

```ts
export type GalleryItem = {
  id: string
  title: string
  description: string
  imageSrc: string
  alt: string
}

export const galleryFiles = [
  { file: "image-1.jpg", title: "Aron Visuals",      description: "Mountain landscape at golden hour",  alt: "Mountain landscape at golden hour" },
  { file: "image-2.jpg", title: "Boudhayan Bardhan", description: "Coastal cliffs and ocean waves",      alt: "Coastal cliffs and ocean waves" },
  { file: "image-3.jpg", title: "Etienne Bosiger",   description: "Forest trail through tall trees",     alt: "Forest trail through tall trees" },
  { file: "image-4.jpg", title: "Filip Zrnzevic",    description: "City skyline at dusk",                alt: "City skyline at dusk" },
  { file: "image-5.jpg", title: "Ivana Cajina",      description: "Desert dunes under a clear sky",      alt: "Desert dunes under a clear sky" },
  { file: "image-6.jpg", title: "Justin Clark",      description: "Snow-capped peaks above a valley",    alt: "Snow-capped peaks above a valley" },
  { file: "image-7.jpg", title: "Meghan Schiereck",  description: "Lush green hills and farmland",       alt: "Lush green hills and farmland" },
  { file: "image-8.jpg", title: "Michael Baccin",    description: "Rocky shoreline at sunset",           alt: "Rocky shoreline at sunset" },
  { file: "image-9.jpg", title: "Quentin",           description: "Misty lake surrounded by mountains",  alt: "Misty lake surrounded by mountains" },
]
```

### `public/images/blocks/gallery-section-v2/image-1.jpg.gitkeep`

```gitkeep
Place your asset here. Same base name, any supported format: jpg, jpeg, png, webp, avif, gif, or svg. Remove the .gitkeep extension from the filename.
```

### `public/images/blocks/gallery-section-v2/image-2.jpg.gitkeep`

```gitkeep
Place your asset here. Same base name, any supported format: jpg, jpeg, png, webp, avif, gif, or svg. Remove the .gitkeep extension from the filename.
```

### `public/images/blocks/gallery-section-v2/image-3.jpg.gitkeep`

```gitkeep
Place your asset here. Same base name, any supported format: jpg, jpeg, png, webp, avif, gif, or svg. Remove the .gitkeep extension from the filename.
```

### `public/images/blocks/gallery-section-v2/image-4.jpg.gitkeep`

```gitkeep
Place your asset here. Same base name, any supported format: jpg, jpeg, png, webp, avif, gif, or svg. Remove the .gitkeep extension from the filename.
```

### `public/images/blocks/gallery-section-v2/image-5.jpg.gitkeep`

```gitkeep
Place your asset here. Same base name, any supported format: jpg, jpeg, png, webp, avif, gif, or svg. Remove the .gitkeep extension from the filename.
```

### `public/images/blocks/gallery-section-v2/image-6.jpg.gitkeep`

```gitkeep
Place your asset here. Same base name, any supported format: jpg, jpeg, png, webp, avif, gif, or svg. Remove the .gitkeep extension from the filename.
```

### `public/images/blocks/gallery-section-v2/image-7.jpg.gitkeep`

```gitkeep
Place your asset here. Same base name, any supported format: jpg, jpeg, png, webp, avif, gif, or svg. Remove the .gitkeep extension from the filename.
```

### `public/images/blocks/gallery-section-v2/image-8.jpg.gitkeep`

```gitkeep
Place your asset here. Same base name, any supported format: jpg, jpeg, png, webp, avif, gif, or svg. Remove the .gitkeep extension from the filename.
```

### `public/images/blocks/gallery-section-v2/image-9.jpg.gitkeep`

```gitkeep
Place your asset here. Same base name, any supported format: jpg, jpeg, png, webp, avif, gif, or svg. Remove the .gitkeep extension from the filename.
```
