Skip to content
Template Empire
Browse components

Hover Zoom Gallery

A free media component for an image gallery hover effect in React, with a slow zoom and contrast-safe caption reveal.

Angular mountain silhouettes beneath a warm evening sunSignal House, Serra
Layered coastal hills reflected in a green-blue inlet
Tidal Rooms, Skagen
Terraced desert mesa under an amber skyMesa Archive, Sonora
Violet alpine peaks beneath a pale northern moon
North Passage, Lofoten
Soft green wetlands rolling towards a low golden horizonReed Pavilion, Norfolk
Slate winter ridges against a cool silver sky
Winter Courtyard, Aomori

Customise

3
1.3333333333333333

Code

<MediaHoverZoom items={items} />

Hover Zoom Gallery is a free image gallery hover effect for Next.js and React, styled with Tailwind and driven entirely by CSS. It reserves every tile's aspect ratio, then eases the image closer while a caption rises over a fully settled gradient scrim.

Linked tiles give keyboard focus the same zoom, caption and visible ring as pointer hover. Non-linked figures, touch devices and no-hover devices keep every caption visible without interaction, while reduced motion removes all scale and translation.

Install

Add the @te registry to your components.json once:

{
  "registries": {
    "@te": "https://templateempire.io/r/{name}.json"
  }
}

Then install the component:

npx shadcn@latest add @te/media-hover-zoom

Usage

import { MediaHoverZoom } from "@/components/media-hover-zoom";

export function Example() {
  return (
    <MediaHoverZoom
      items={[
        {
          id: "cliff-house",
          src: "/cliff-house.jpg",
          alt: "Concrete house set into a coastal cliff",
          caption: "Cliff House, Madeira",
          href: "/projects/cliff-house",
        },
        {
          id: "courtyard",
          src: "/courtyard.jpg",
          alt: "Sunlit stone courtyard planted with olive trees",
          caption: "Olive Courtyard, Puglia",
        },
        {
          id: "lake-studio",
          src: "/lake-studio.jpg",
          alt: "Timber studio reflected in a still mountain lake",
          caption: "Lake Studio, Cumbria",
        },
      ]}
      columns={3}
      aspectRatio={4 / 3}
    />
  );
}

Props

PropTypeDefaultDescription
items{ id: string; src: string; alt: string; caption: string; href?: string }[]requiredThree to nine images in display order. The stable id preserves tile identity when the array is reordered.
columns2 | 33Number of columns used from the medium breakpoint upwards.
aspectRationumber4 / 3Positive width divided by height ratio reserved for every tile to prevent layout shift.
classNamestringundefinedAdditional classes merged onto the gallery grid.

Customisation

Set columns and aspectRatio to reshape the gallery without changing its content. Two Tier 3 tokens control the caption band: --media-hover-zoom-scrim is the ink the gradient fades in over the image, and --media-hover-zoom-caption is the caption colour that sits on it. Both are theme-stable by default, because a photograph does not invert when the page does, and together they hold the caption above 4.5:1 over any image. Override them in a matched pair, so the caption keeps a backdrop it is readable on. --primary and --primary-foreground are exactly that pair, and both exist in a plain shadcn install:

<MediaHoverZoom
  items={projects}
  columns={2}
  aspectRatio={16 / 10}
  className="[--media-hover-zoom-scrim:var(--primary)] [--media-hover-zoom-caption:var(--primary-foreground)]"
/>

Frequently asked questions

Is Hover Zoom Gallery free for commercial use?
Yes. Hover Zoom Gallery is MIT licensed, so you can use it in personal and commercial projects without attribution.
Is the image gallery hover effect accessible?
Yes. Linked tiles are single anchors named by their captions, preserve meaningful image alt text and show the same treatment for keyboard focus as pointer hover. Figures remain non-interactive with visible figcaptions, and touch or reduced-motion users never need hover to discover content.
How do I customise the gallery layout and caption backing?
Choose two or three columns, pass a positive aspectRatio for the tile shape, and override --media-hover-zoom-scrim and --media-hover-zoom-caption as a pair to retint the caption band. Point them at tokens your app defines, such as --primary and --primary-foreground, so the override still resolves in a plain shadcn install. The component uses semantic border and ring colours throughout, and the caption band keeps both of its inks theme-stable so the image reads the same on a light page as on a dark one.

Related components

Want the full page this belongs to?Template Empire builds complete Next.js templates with this motion system throughout. Browse the paid templates that pair with these free components.Explore Template Empire templates