Back to library
carousels
Hover Expand Gallery
A row of slim image cards that smoothly expand on hover or tap, revealing a gradient overlay and caption for the active image.
Inspired by Skiper UI · View source
Preview
# 2
Copy for AI
Copy for Lovable is the fastest way to move this component into another Lovable project — it copies the full prompt with dependencies, files, and a usage example. Copy all files gives you the raw source if you want to paste it manually.
Installation
npx shadcn@latest add @skiper-ui/skiper52Paste into a terminal — or paste into a Lovable chat and I'll run it for you.
Source files
components/ui/hover-expand.tsx
"use client";
import { AnimatePresence, motion } from "framer-motion";
import React, { useState } from "react";
import { cn } from "@/lib/utils";
export type HoverExpandImage = {
src: string;
alt: string;
code: string;
};
const HoverExpand_001 = ({
images,
className,
}: {
images: HoverExpandImage[];
className?: string;
}) => {
const [activeImage, setActiveImage] = useState<number | null>(1);
return (
<motion.div
initial={{ opacity: 0, translateY: 20 }}
animate={{ opacity: 1, translateY: 0 }}
transition={{
duration: 0.3,
delay: 0.5,
}}
className={cn("relative w-full max-w-6xl px-5", className)}
>
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ duration: 0.3 }}
className="w-full"
>
<div className="flex w-full items-center justify-center gap-1">
{images.map((image, index) => (
<motion.div
key={index}
className="relative cursor-pointer overflow-hidden rounded-3xl"
initial={{ width: "2.5rem", height: "20rem" }}
animate={{
width: activeImage === index ? "24rem" : "5rem",
height: "24rem",
}}
transition={{ duration: 0.3, ease: "easeInOut" }}
onClick={() => setActiveImage(index)}
onHoverStart={() => setActiveImage(index)}
>
<AnimatePresence>
{activeImage === index && (
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
className="absolute h-full w-full bg-gradient-to-t from-black/40 to-transparent"
/>
)}
</AnimatePresence>
<AnimatePresence>
{activeImage === index && (
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
className="absolute flex h-full w-full flex-col items-end justify-end p-4"
>
<p className="text-left text-xs text-white/50">
{image.code}
</p>
</motion.div>
)}
</AnimatePresence>
<img
src={image.src}
className="size-full object-cover"
alt={image.alt}
/>
</motion.div>
))}
</div>
</motion.div>
</motion.div>
);
};
export { HoverExpand_001 };
/**
* Skiper 52 HoverExpand_001 — React + Framer Motion
* Author: @gurvinder-singh02 — https://gxuri.me
* Attribution to Skiper UI is required when using the free version.
*/
Dependencies
framer-motion