How to make a simple image gallery and modal component in React with Tailwind CSS
After figuring out different approaches to making an image gallery with a modal window to view the images, I have decided to create a version with just React and Tailwind CSS. Here is the final code with a few adjustments.
Gallery.jsx file
import { useState } from "react";
import Modal from "./Modal";
export default function Gallery({images}) {
const [modalOpen, setModalOpen] = useState(false);
const [currentImage, setCurrentImage] = useState(0);
return (
<section className="m-10 grid grid-cols-1 justify-items-center gap-10 sm:grid-cols-2 lg:grid-cols-3">
{images.map((image) => (
<button
key={image.id}
className="cursor-pointer"
onClick={()=> {
setModalOpen(true);
setCurrentImage(image.id);
}}
>
<img
src={image.src}
alt={`Image ${image.id + 1}`}
className="h-80 w-60 rounded object-cover"
/>
</button>
))}
{modalOpen && (
<Modal
setModalOpen={setModalOpen}
setCurrentImage={setCurrentImage}
images={images}
currentImage={currentImage}
/>
)}
</section>
);
}
images.js file
import img1 from "./assets/images/1.jpg";
import img2 from "./assets/images/2.jpg";
import img3 from "./assets/images/3.jpg";
import img4 from "./assets/images/4.jpg";
import img5 from "./assets/images/5.jpg";
export const images = [
{ id: 0, src: img1, alt: "Image 0 description" },
{ id: 1, src: img2, alt: "Image 1 description" },
{ id: 2, src: img3, alt: "Image 2 description" },
{ id: 3, src: img4, alt: "Image 3 description" },
{ id: 4, src: img5, alt: "Image 4 description" },
];
MainComponent.jsx file
import Gallery from "./components/Gallery";
import { images } from "./images";
export default function MainComponent() {
return <Gallery images={images} />;
}
Modal.jsx file
export default function Modal({
setModalOpen,
setCurrentImage,
images,
currentImage,
}) {
return (
<section className="fixed inset-0 z-50 flex items-center justify-center bg-black/80">
<div className="mx-4 max-h-[90vh] w-full max-w-4xl overflow-y-auto rounded bg-white p-6">
<button
onClick={()=> setModalOpen(false)}
className="absolute top-4 right-4 cursor-pointer text-2xl text-gray-100 hover:text-gray-300"
aria-label="Close modal"
>
×
</button>
<div className="relative">
<img
src={images[currentImage].src}
alt={`Image ${currentImage + 1}`}
className="mx-auto h-80 w-60 rounded object-cover sm:size-full"
/>
<div className="my-6 flex justify-center space-x-2">
{images.map((image) => (
<button
key={image.id}
onClick={()=> setCurrentImage(image.id)}
className={`h-3 w-3 cursor-pointer rounded-full ${
currentImage= image.id ? "bg-black" : "bg-gray-300"
}`}
/>
))}
</div>
<button
onClick={()=>
setCurrentImage((prev)=>
prev > 0 ? prev - 1 : images.length - 1,
)
}
className="absolute top-1/2 left-2 -translate-y-1/2 cursor-pointer rounded-full bg-black/50 px-2 pb-3 text-4xl text-white hover:bg-black/60"
>
‹
</button>
<button
onClick={()=>
setCurrentImage((prev)=>
prev < images.length - 1 ? prev + 1 : 0,
)
}
className="absolute top-1/2 right-2 -translate-y-1/2 cursor-pointer rounded-full bg-black/50 px-2 pb-3 text-4xl text-white hover:bg-black/60"
>
›
</button>
</div>
</div>
</section>
);
}
Live version
This version uses the images from my blog posts as examples (click on the images to open the modal window).
Styling has been slightly modified to match the design of the current site.
Thank you for reading.