# Gallery Section v3

> A centered painting-gallery intro with a five-tile asymmetric bento grid and hover overlays for title, notes, and price.

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-v3).

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

## Install

```bash
npx shadcn@latest add https://shadcn.exchange/r/uiception/gallery-section-v3.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-v3/page.tsx`

```tsx
import GallerySectionV3 from "./components/gallery-section-v3"

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

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

```tsx
import { GalleryBentoCard } from "./gallery-bento-card"
import { createBlockImage } from "@/lib/block-media"
import { galleryFiles, sectionMeta, type GalleryItem } from "../lib/config"
import "../styles/gallery-section-v3.css"

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

export type GallerySectionV3Props = {
  header?: { title: string; description?: string } | false
  items?: GalleryItem[]
}

export default function GallerySectionV3({
  header = sectionMeta,
  items = galleryItems,
}: GallerySectionV3Props = {}) {
  return (
    <section className="py-4 md:py-6 lg:py-8">
      <div className="mx-auto max-w-7xl px-4">
        {header !== false ? (
          <div className="mx-auto mb-10 max-w-2xl text-center md:mb-12">
            <h2 className="text-4xl font-medium leading-[1.1] tracking-[-0.03em] lg:text-5xl">
              {header.title}
            </h2>
            {header.description ? (
              <p className="mt-4 text-[0.9375rem] leading-relaxed text-muted-foreground">
                {header.description}
              </p>
            ) : null}
          </div>
        ) : null}
        <div className="gsv3-bento">
          {items.map((item, index) => (
            <GalleryBentoCard
              key={item.id}
              item={item}
              priority={index === 0}
            />
          ))}
        </div>
      </div>
    </section>
  )
}
```

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

```tsx
"use client"

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

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

type GalleryBentoCardProps = {
  item: GalleryItem
  priority?: boolean
}

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

  return (
    <article className="group relative isolate overflow-hidden rounded-lg bg-muted">
      <Image
        src={item.imageSrc}
        alt={item.alt}
        fill
        unoptimized
        priority={priority}
        sizes="(max-width: 768px) 100vw, 50vw"
        className={`object-cover transition-opacity duration-500${loaded ? " opacity-100" : " opacity-0"}`}
        onLoad={() => setLoaded(true)}
        onError={() => setLoaded(true)}
      />
      <div
        className="pointer-events-none absolute inset-0 flex flex-col justify-end bg-linear-to-t from-black/75 via-black/25 to-transparent p-4 opacity-0 transition-opacity duration-300 group-hover:opacity-100 group-focus-within:opacity-100 md:p-5"
      >
        <h3 className="text-base font-medium tracking-tight text-white md:text-lg">
          {item.title}
        </h3>
        <p className="mt-1 text-sm leading-snug text-white/80">
          {item.description}
        </p>
        <p className="mt-2 font-mono text-sm text-white">{item.price}</p>
      </div>
    </article>
  )
}
```

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

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

export const sectionMeta = {
  title: "Painting Gallery",
  description:
    "Original works on canvas and paper",
} as const

export const galleryFiles = [
  {
    file: "image-1.jpg",
    title: "Morning harbor",
    description: "Oil on linen, 36 × 48 in",
    price: "$4,800",
    alt: "Oil painting of a morning harbor",
  },
  {
    file: "image-2.jpg",
    title: "Field study no. 7",
    description: "Acrylic on panel, 18 × 24 in",
    price: "$1,250",
    alt: "Acrylic field study painting",
  },
  {
    file: "image-3.jpg",
    title: "Quiet interior",
    description: "Oil on canvas, 24 × 30 in",
    price: "$2,900",
    alt: "Oil painting of a quiet interior",
  },
  {
    file: "image-4.jpg",
    title: "Coastal light",
    description: "Watercolor on paper, 12 × 16 in",
    price: "$780",
    alt: "Watercolor of coastal light",
  },
  {
    file: "image-5.jpg",
    title: "Autumn orchard",
    description: "Oil on canvas, 40 × 40 in",
    price: "$6,200",
    alt: "Oil painting of an autumn orchard",
  },
] as const
```

### `registry/new-york/blocks/gallery-section-v3/styles/gallery-section-v3.css`

```css
.gsv3-bento {
  display: grid;
  gap: 0.75rem;
  grid-template-columns: 1fr;
}

.gsv3-bento > * {
  min-height: 16rem;
}

@media (min-width: 768px) {
  .gsv3-bento {
    grid-template-columns: repeat(4, minmax(0, 1fr));
    grid-template-rows: repeat(2, minmax(15rem, 1fr));
    gap: 1rem;
    min-height: 34rem;
  }

  .gsv3-bento > * {
    min-height: 0;
  }

  /* [1][2][3][3]
     [1][4][4][5] */
  .gsv3-bento > :nth-child(1) {
    grid-column: 1;
    grid-row: 1 / span 2;
  }

  .gsv3-bento > :nth-child(2) {
    grid-column: 2;
    grid-row: 1;
  }

  .gsv3-bento > :nth-child(3) {
    grid-column: 3 / span 2;
    grid-row: 1;
  }

  .gsv3-bento > :nth-child(4) {
    grid-column: 2 / span 2;
    grid-row: 2;
  }

  .gsv3-bento > :nth-child(5) {
    grid-column: 4;
    grid-row: 2;
  }
}
```

### `public/images/blocks/gallery-section-v3/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-v3/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-v3/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-v3/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-v3/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.
```
