Have you ever clicked on an image on a webpage that opens up a larger version of the image with navigation to view other photos?
Some folks call it a pop-up. Others call it a lightbox. Bootstrap calls it a modal. I mention Bootstrap because I want to use it to make the same sort of thing. So, let’s call it a modal from here on out.
Why Bootstrap? you might ask. Well, a few reasons:
- I’m already using Bootstrap on the site where I want this effect, so there’s no additional overhead in terms of loading resources.
- I want something where I have complete and easy control over aesthetics. Bootstrap is a clean slate compared to most modal plugins I’ve come across.
- The functionality I need is fairly simple. There isn’t much to be gained by coding everything from scratch. I consider the time I save using the Bootstrap framework to be more beneficial than any potential drawbacks.
Here’s where we’ll end up:
Let’s go through that, bit by bit.
Step 1: Create the image gallery grid
Let’s start with the markup for a grid layout of images. We can use Bootstrap’s grid system for that.
<div class="row" id="gallery">
<div class="col-12 col-sm-6 col-lg-3">
<img class="w-100" src="/image-1">
</div>
<div class="col-12 col-sm-6 col-lg-3">
<img class="w-100" src="/image-2">
</div>
<div class="col-12 col-sm-6 col-lg-3">
<img class="w-100" src="/image-3">
</div>
<div class="col-12 col-sm-6 col-lg-3">
<img class="w-100" src="/image-4">
</div>
</div>
Now we need data attributes to make those images interactive. Bootstrap looks at data attributes to figure out which elements should be interactive and what they should do. In this case, we’ll be creating interactions that open the modal component and allow scrolling through the images using the carousel component.
About those data attributes:
- We’ll add
data-toggle="modal"
anddata-target="#exampleModal"
to the parent element(#gallery
). This makes it so clicking anything in the gallery opens the modal. We should also add the data-target value (#exampleModal
) as the ID of the modal itself, but we’ll do that once we get to the modal markup. - Let’s add
data-target="#carouselExample"
and adata-slide-to
attribute to each image. We could add those to the image wrappers instead, but we’ll go with the images in this post. Later on, we’ll want to use the data-target value (#carouselExample
) as the ID for the carousel, so note that for when we get there. The values fordata-slide-to
are based on the order of the images.
Here’s what we get when we put that together:
<div class="row" id="gallery" data-toggle="modal" data-target="#exampleModal">
<div class="col-12 col-sm-6 col-lg-3">
<img class="w-100" src="/image-1.jpg" data-target="#carouselExample" data-slide-to="0">
</div>
<div class="col-12 col-sm-6 col-lg-3">
<img class="w-100" src="/image-2.jpg" data-target="#carouselExample" data-slide-to="1">
</div>
<div class="col-12 col-sm-6 col-lg-3">
<img class="w-100" src="/image-3.jpg" data-target="#carouselExample" data-slide-to="2">
</div>
<div class="col-12 col-sm-6 col-lg-3">
<img class="w-100" src="/image-4.jpg" data-target="#carouselExample" data-slide-to="3">
</div>
</div>
Interested in knowing more about data attributes? Check out the CSS-Tricks guide to them.
Step 2: Make the modal work
This is a carousel inside a modal, both of which are standard Bootstrap components. We’re just nesting one inside the other here. Pretty much a straight copy-and-paste job from the Bootstrap documentation.
Here’s some important parts to watch for though:
- The modal ID should match the
data-target
of the gallery element. - The carousel ID should match the
data-target
of the images in the gallery. - The carousel slides should match the gallery images and must be in the same order.
Here’s the markup for the modal with our attributes in place:
<!-- Modal markup: https://getbootstrap.com/docs/4.4/components/modal/ -->
<div class="modal fade" id="exampleModal" tabindex="-1" role="dialog" aria-hidden="true">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
<!-- Carousel markup goes here -->
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
</div>
</div>
</div>
</div>
We can drop the carousel markup right in there, Voltron style!
<!-- Modal markup: https://getbootstrap.com/docs/4.4/components/modal/ -->
<div class="modal fade" id="exampleModal" tabindex="-1" role="dialog" aria-hidden="true">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
<!-- Carousel markup: https://getbootstrap.com/docs/4.4/components/carousel/ -->
<div id="carouselExample" class="carousel slide" data-ride="carousel">
<div class="carousel-inner">
<div class="carousel-item active">
<img class="d-block w-100" src="/image-1.jpg">
</div>
<div class="carousel-item">
<img class="d-block w-100" src="/image-2.jpg">
</div>
<div class="carousel-item">
<img class="d-block w-100" src="/image-3.jpg">
</div>
<div class="carousel-item">
<img class="d-block w-100" src="/image-4.jpg">
</div>
</div>
<a class="carousel-control-prev" href="#carouselExample" role="button" data-slide="prev">
<span class="carousel-control-prev-icon" aria-hidden="true"></span>
<span class="sr-only">Previous</span>
</a>
<a class="carousel-control-next" href="#carouselExample" role="button" data-slide="next">
<span class="carousel-control-next-icon" aria-hidden="true"></span>
<span class="sr-only">Next</span>
</a>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
</div>
</div>
</div>
</div>
Looks like a lot of code, right? Again, it’s basically straight from the Bootstrap docs, only with our attributes and images.
Step 3: Deal with image sizes
This isn’t necessary, but if the images in the carousel have different dimensions, we can crop them with CSS to keep things consistent. Note that we’re using Sass here.
// Use Bootstrap breakpoints for consistency.
$bootstrap-sm: 576px;
$bootstrap-md: 768px;
$bootstrap-lg: 992px;
$bootstrap-xl: 1200px;
// Crop thumbnail images.
#gallery {
img {
height: 75vw;
object-fit: cover;
@media (min-width: $bootstrap-sm) {
height: 35vw;
}
@media (min-width: $bootstrap-lg) {
height: 18vw;
}
}
}
// Crop images in the coursel
.carousel-item {
img {
height: 60vw;
object-fit: cover;
@media (min-width: $bootstrap-sm) {
height: 350px;
}
}
}
Step 4: Optimize the images
You may have noticed that the markup uses the same image files in the gallery as we do in the modal. That doesn’t need to be the case. In fact, it’s a better idea to use smaller, more performant versions of the images for the gallery. We’re going to be blowing up the images to their full size version anyway in the modal, so there’s no need to have the best quality up front.
The good thing about Bootstrap’s approach here is that we can use different images in the gallery than we do in the modal. They’re not mutually exclusive where they have to point to the same file.
So, for that, I’d suggest updating the gallery markup with lower-quality images:
<div class="row" id="gallery" data-toggle="modal" data-target="#exampleModal">
<div class="col-12 col-sm-6 col-lg-3">
<img class="w-100" src="/image-1-small.jpg" data-target="#carouselExample" data-slide-to="0">
<!-- and so on... -->
</div>
That’s it!
The site where I’m using this has already themed Bootstrap. That means everything is already styled to spec. That said, even if you haven’t themed Bootstrap you can still easily add custom styles! With this approach (Bootstrap vs. plugins), customization is painless because you have complete control over the markup and Bootstrap styling is relatively sparse.
Here’s the final demo:
This is a really nice tutorial, the result seems broken on my pixel 2xl display though. The card works just fine but the carousel on the result tab doesn’t works, I just get the images displayed vertically.
Hey Damian, thanks for reading. On small screens, the initial photo grid is a single column. And if you tap/click one of the images it’ll open the carousel. On larger screens the photo grid has two or four columns depending on screen size. Here’s a quick demo video showing how it works: https://www.loom.com/share/eb637f33edc84b0c9644b93c8b78f221
Are you seeing something different on your end?
You where right Diego, my bad. I don’t now why I didn’t tap on the image to toggle the modal ♂️. Great job, thank you!
You’re welcome! Glad it’s working.
Thank you so much for this.
My carousel is showing up at the bottom of the page though – how can I hide it until someone clicks on the images?
Hi Joy. Did you add the modal classes and data attributes to the carousel or its container?
Diego, would you make a video tutorial of this? I couldn’t quite get it to work. I’m fairly new to coding.
Hi Alex, I’d be happy to help. Can you explain what problem you’re having or what you need clarification about?
Thank you for this Diego could not get it to work though.
What i did:
1. created the html file with the image gallery grid and modal makeup
only change was location of picture to use (used ones on my local system)
I had bootstrap ref already
(
)
Copied the scss file and use sass to convert it to css file (no change to this)
the js file was added to the html after the body tag
Problem:
I can not click on the picture
What am I doing wrong ?
thanks
Hi Deji,
Try adding a simple modal component to the page (or a new page). Just a button that opens a demo modal will work. That will help you isolate the problem. If that doesn’t work, there may be an issue with Bootstrap on your site. If it does work, you can add the pieces of the modal gallery one step at a time to see when it stops working. For example, change the button to an image, then add multiple images, then add the carousel, etc. Good luck!
Hi, I’m using a grid in a foreach loop but every time I click on the image to open the modal, it opens the first item instead of the item I clicked on. Do you know what could be causing this?
Hi Andy, sounds like you may be missing some data attributes. Each grid item needs to have a
data-target
anddata-slide-to
attribute. Thedata-target
value should be theid
of your carousel (same for all grid items), and thedata-slide-to
value should be the index of the corresponding carousel slide (unique for each grid item), starting with 0. Check the code snippets in Step 1 for an example. Hope that helps.Great job Diego, worked fine on my page, i just want to know if there’s a way to show the custom styling by default without button
Thanks George, glad it’s working for you. You can style the components however you like. The button is just for the demo, to show the difference between the default styling and an example of custom styling.
If you want to use the custom styling from the demo, you can simply remove
.custom
from lines 53 and 67 in the CSS.Thanks a lot for this tutorial. I do have a problem, whenever I clicked 1 to 3 images it will show the correct images for each image however more than that it’s not working anymore. For example, if I clicked #1 then it will show #1 but if I clicked #4, it will show me the last opened image. Can you help me?
Hi zizi, you may need to check the
data-slide-to
attributes on your gallery images. Each image should have this set to the corresponding slide in the carousel, starting with zero for the first one. So for image #1 it’ll be0
, and for image #4 it’ll be3
.