Image Gallery
A grid of photo thumbnails that opens a full-screen viewer (lightbox). Learn how thumbnails and full-size images should load, how swiping, zooming and closing should behave, and the focus, counter and data bugs testers find in almost every gallery.
You’re flat hunting. You scroll down to the photos of a promising listing, filter to Outside and tap the balcony. The app shows you the living room instead. You close it, and you’re back at the very top of the page. You scroll down again, open the bedroom, zoom in on the wardrobe, tap Next, and the bathroom appears already zoomed into a corner. Meanwhile your data plan has quietly paid for 40 MB of photos you only saw as tiny squares.
An image gallery looks simple: a grid of small pictures, and a big picture when you tap one. But it combines image loading, gestures, a modal viewer, focus management and state (which photo, which filter, how far you scrolled). Every one of those can break, and users notice right away.
What is it?
An image gallery has two parts:
- A thumbnail grid: small, usually cropped previews of many photos (and sometimes videos).
- A viewer (often called a lightbox): a full-screen layer that shows one photo at a time, whole and sharp. You swipe between photos, zoom in, and close it to get back to the grid.
You meet galleries in property and hotel listings, product pages, social profiles, chat apps (“Media, links and docs”), review sections and of course the phone’s own Photos app.
Anatomy
- 1
Thumbnail grid
Small, cropped previews. Each thumbnail is a button (or link) named by the photo’s alt text.
- 2
Video item
Marked with a play icon and duration, so users know it is not a photo.
- 3
Viewer (lightbox)
A modal dialog over the page. Focus moves into it and stays there until it closes.
- 4
Close
Always visible. Esc, swipe down and the system Back do the same.
- 5
Counter
“3 of 12”. Must match the photo shown, and its total must match the grid.
- 6
Share / save
Opens the system share sheet or saves the photo. Optional, but it must share the right photo.
- 7
Full photo
The whole image, uncropped and sharp. Pinch or double-tap to zoom.
- 8
Previous / next
Button alternative to swiping (WCAG 2.5.1). Disabled at the first and last photo.
- 9
Caption and zoom buttons
Caption describes the photo for everyone; zoom buttons are the alternative to pinching.
How it differs from a carousel
A carousel shows slides in the page, one at a time, often with autoplay. A gallery shows many thumbnails at once and opens them in a separate viewer. Many product pages combine both: a small swipeable carousel on the page, and a tap on it opens the full-screen viewer described here. Uploading photos is a different story too: see Image upload for cameras, permissions, HEIC and EXIF rotation.
| Carousel | Image gallery | |
|---|---|---|
| Shows | One slide in the page | Many thumbnails, then one photo full screen |
| Moves by itself | Sometimes (autoplay) | Never |
| Typical content | Banners, product photos | Listing photos, albums, user media |
| Main risks | Autoplay, gestures, dots | Loading, zoom, closing, focus |
Designs & variants
Galleries come in a few common layouts. They all open the same kind of viewer. Try each one: tap a photo, swipe or drag sideways, double-tap to zoom, then close it (X, Esc or a swipe down) and check where you land.
Photos apps and profiles: dense, every photo cropped to a square. Fast to scan, but portraits and panoramas lose their edges.
Riverside flat · 12 photos
Tap a photo. Swipe or drag sideways, double-tap to zoom, swipe down or press Esc to close.
When to use it
- Users want to browse many photos of the same thing and pick which ones to look at: a flat, a hotel room, a used car, a restaurant’s dishes.
- Photos need to be seen large and in detail (zoom into a floor plan, a scratch on a used phone, the fabric of a jacket).
- Users collect media themselves: albums, chat media, a profile’s posts.
Good and bad uses
Important info as text
“Ground floor, no lift, 64 m²” written in the listing, and the floor plan photo as an extra.
Important info only in photo 9
The only mention that there is no lift is a sign in the background of the 9th photo. Most users never open it, and screen reader users can’t read it at all.
One clear photo when one is enough
A profile picture, a single product shot or an avatar opens in a simple zoomable view (or not at all).
A gallery for one image
A full grid + viewer with “1 of 1”, swipe hints and a share bar around a single logo.
How good galleries behave
Small thumbnails, sharp viewer
A camera photo is often 4000 × 3000 px and 3–5 MB. A thumbnail on a phone is about 120 CSS px wide. Loading the original for every thumbnail is one of the most common gallery bugs: 12 photos are 40 MB, slow on a train and expensive on a data plan.
Good galleries load two versions:
- Thumbnails in the grid: small files (roughly 15–60 KB), sized for the grid cell and the screen’s pixel density.
- A large version in the viewer, loaded when the photo is opened, plus the next one in advance (preloading) so swiping feels instant. Not the 4000 px original either: around the screen width is enough.
On the web this is done with srcset and sizes: the page offers several widths of the same photo, says how wide it will be shown, and the browser picks the smallest file that is still sharp. Thumbnails further down use lazy loadingLazy loadingLoading images or content only when the user scrolls near them.Open in glossary → (loading="lazy") so they load only when you scroll near them. Apps do the same with image libraries and server-side resizing. Try it:
Dev note
Screen width (CSS px)
Pixel density
Image shown in
<img
src="kitchen-600.jpg"
srcset="kitchen-300.jpg 300w,
kitchen-600.jpg 600w,
kitchen-1200.jpg 1200w,
kitchen-2400.jpg 2400w"
sizes="(min-width: 768px) 25vw, 33vw"
width="1500" height="1000"
loading="lazy"
alt="Open kitchen with white cabinets…">Slot: 129 CSS px wide (3 columns)
× 3x density = needs about 386 real pixels
- kitchen-300.jpg22 KB
- kitchen-600.jpg70 KB
- kitchen-1200.jpg240 KB
- kitchen-2400.jpg820 KB
- original 4032 px3.4 MB
The browser downloads kitchen-600.jpg, 98% less than the original.
Things to check: count the image requests and total MB in the network log when the gallery opens; check that the viewer image is sharp on a high-density phone (not the thumbnail stretched up); and that images also have width/height (or a fixed aspect ratio box), so the page doesn’t jump while they load.
Crop in the grid, whole photo in the viewer
Grid cells are usually all the same shape, so photos are cropped to fill them. That’s fine for a preview, but it means portraits lose their top and bottom, and a panorama loses most of its width. In the variants above, compare the balcony panorama in the square grid (the boat on the right is gone) with the justified rows.
Dev note
object-fit: cover.The viewer must show the whole photo, never cropped: black or blurred bars around it are expected. Also check that photos are not stretched (wrong aspect ratio), that portrait photos aren’t sideways (EXIF orientation, see Image upload), and that the crop never cuts off what the photo is about.
Dev note
object-fit: contain.Swiping between photos
- Swipe left and right on touch, drag or arrow buttons with a mouse, arrow keys on a keyboard.
- The ends are clear: on the first photo, Previous is disabled; on the last, Next is disabled, and a swipe past the end bounces back (rubber band). It never shows an empty slide. Some viewers loop from the last photo to the first: fine, if it’s seamless and the counter says so.
- Only horizontal swipes change the photo. A swipe that is mostly vertical belongs to “swipe down to close” (or does nothing), and a small wobble is not a swipe (the touch slopTouch slopThe small distance a finger may move before a touch counts as movement instead of a tap.Open in glossary →).
- A video in the gallery doesn’t start playing while you swipe past it, and stops when you swipe away.
Zoom and pan
- Pinch with two fingers, and double-tap (double-click on the web) to zoom into that spot; double-tap again to zoom back out. On the web, a viewer should also offer zoom buttons and keys.
- Limits: zoom stops at a sensible maximum (often 3–5×) and can’t go smaller than “fit to screen”. While zoomed, a one-finger drag pans the photo, but only up to its edges: you should never pan into empty black space.
- Zoom resets when you move to another photo. If photo 4 opens already zoomed into a corner because you zoomed photo 3, that’s a bug.
- While zoomed, a drag pans the photo instead of swiping to the next one. Many apps switch to the next photo only when you’re already at the edge of the zoomed photo, so check that at the edges too.
Closing and coming back
Users close a viewer in many ways, and every one must work and land in the same place:
- The X button (always visible, at least 44×44 pt on iOS and 48×48 dp on Android. On the web, WCAG 2.5.8 (AA) asks for at least 24×24 CSS px; 44×44 px (2.5.5, AAA) is the comfortable target).
- Esc on a keyboard.
- Swipe down (common on iOS and Android): the photo follows your finger, the background fades, and letting go far enough closes it; a short drag snaps back.
- The system Back (Android Back button or gesture, browser Back on the web) closes the viewer, not the whole screen.
After closing, the grid is exactly where you left it: same scroll position, same filter, and focus on the thumbnail of the photo you were last looking at (photo 7 if you swiped from 3 to 7). On iOS and Android, the viewer often shrinks back into that thumbnail, which only works if the app knows which one it is.
Counter, captions and alt text
- The counter (“3 of 12”) is 1-based, matches the photo shown, and its total matches the photos the user can actually swipe through. If the grid was filtered to 3 outside photos, the viewer shows those 3 (“1 of 3”), and tapping a thumbnail always opens that photo.
- A caption (“Balcony with a view over the river”) helps everyone; long captions wrap or expand, they don’t run under the buttons.
- Every photo needs alt textAlt textA text description of an image that screen readers read aloud.Open in glossary → that describes what matters for the task: “Floor plan: living room 24 m², bedroom 14 m²…” is useful; “IMG_4032.jpg” or “photo” is not. The thumbnail button’s name is the alt text, plus the position if that helps (“Photo 3 of 12: …”).
Video items
Galleries often mix in videos (a video tour, a short clip in a chat). Check that the thumbnail shows it’s a video (play icon, duration), that it does not autoplay with sound in the viewer, that swiping away stops it, that it has controls (play, pause, sound) and captionsCaptionsOn-screen text for the speech and important sounds in a video.Open in glossary → when there’s speech, and that the network doesn’t download the whole video until the user presses Play.
Sharing and saving
If the viewer offers Share or Save: it must share the photo currently shown (not the first one, not the thumbnail-size version), open the system share sheet on mobile, and saving to the photo library must ask for permission the right way (on iOS, the “add photos only” permission; see App permissions). Shared links should open the same photo in the listing (a deep linkDeep linkA link that opens a specific screen inside an app instead of just its home screen.Open in glossary → like ?photo=7).
Slow networks: placeholders and blur-up
On a slow connection the grid must not look broken. Good options:
- Placeholders with the right size (a grey box or the photo’s dominant color), so nothing jumps.
- Blur-up: show a tiny, blurred version first (a few hundred bytes, sometimes embedded in the page), then fade in the sharp image.
- In the viewer: show the thumbnail (already loaded) scaled up and blurred, plus a loading indicator, until the large version arrives. Never a blank black screen with no feedback.
Test with network throttlingNetwork throttlingDeliberately slowing the connection to test how an app behaves on poor networks.Open in glossary →: DevTools presets “Slow 4G” and “3G”, the Network Link Conditioner on iOS, or a proxy (see Network conditions).
Common bugs
| # | Bug | What you see | Typical severity |
|---|---|---|---|
| 1 | Lost place after closing | Grid jumps to the top, filter resets, focus lost | Medium |
| 2 | Wrong photo opens | After filter or sort, a thumbnail opens a different photo | High |
| 3 | Empty slide at the end | Swipe past the last photo shows a black screen | Low–Medium |
| 4 | Zoom carries over | Next photo opens zoomed into a corner | Low |
| 5 | Counter off by one | “0 of 12”, or “13 of 12” | Low |
| 6 | Originals as thumbnails | 40 MB on open, slow grid | Medium–High |
| 7 | Viewer shows a blurry thumbnail | Large version never loads | Medium |
| 8 | Pan beyond the edges | You can drag the zoomed photo into black space | Low |
| 9 | Back leaves the screen | Android/browser Back closes the listing, not the viewer | Medium |
| 10 | No keyboard or screen reader access | No focus in the viewer, Esc does nothing, photos unnamed | High |
Stress-test it
This is a correct gallery inside a listing screen. Switch features on and off and watch the grid, the network log and the screen reader text. Turn on Slow network to see placeholders and blur-up, turn off Sized thumbnails to see what originals cost, scroll the grid with lazy loading on and off, compare cropped and whole thumbnails, and in the viewer try zooming, the Back button below the phone and Reduced motion.
Blur-up only shows while images load: turn on Slow network (3G). Reduced motion and zoom buttons change the viewer: open a photo.
Riverside flat
€1,450 / month
2 rooms · 64 m² · 3rd floor with lift
Bright flat with a balcony over the river, a renovated kitchen and a quiet bedroom facing the courtyard.
Photos (12)
Listed 3 days ago · Ref. RF-2231
Screen reader
“Photos of Riverside flat. 12 photos.”
Network · images0 req · 0 KB
Find the bug
This property app’s listing has a photo gallery with six bugs. Explore it like a tester:
- Scroll down to the photos, open one, then close it. Where are you?
- Swipe (or tap the arrows) to the last photo, and then once more.
- Double-tap a photo to zoom, then tap Next.
- Watch the counter on the first and last photo.
- Choose the Outside filter and tap the first photo.
- Read the Network log.
🐞 Bug hunt: Nestly listing
This demo has 6 hidden bugs. Explore it like a tester, then check your findings.
€1,450 / month
Bright 2-room flat, Riverside
Quay Street 14 · 64 m² · 3rd floor
About this flat
Renovated in 2024, with oak floors, a balcony over the river and a quiet bedroom facing the green courtyard. Lift, bike room and storage in the basement. Available from 1 December.
Photos
Book a viewing
Next free slots: Thu 17:30, Sat 10:00
Scroll the screen, tap a photo. Swipe or drag, double-tap to zoom, use the arrows, swipe down or press Esc to close.
Network (screen open)0 req · 40.0 MB
- 1
Hint: Scroll down to the photos, open one, close it.
- 2
Hint: Go to the floor plan and hallway at the end, then swipe or tap Next once more.
- 3
Hint: Double-tap (double-click) a photo to zoom, then tap the Next arrow.
- 4
Hint: Open the first photo and read the counter. Then the last one.
- 5
Hint: Choose Outside, then tap the first photo (the balcony).
- 6
Hint: Read the Network log when the screen opens.
How to test it
Checklist
Image gallery test checklist
0 of 17 checked
Example test cases
TC-GAL-001Tapping a thumbnail after filtering opens the same photopositivehigh
Preconditions
- Listing with 12 photos, 3 of them in “Outside”
Steps
- Choose the Outside filter
- Tap the 2nd thumbnail
- Swipe through all photos in the viewer
Expected result
The 2nd outside photo opens with counter “2 of 3”. Swiping only shows the 3 outside photos.
TC-GAL-002Closing the viewer returns to the same placepositivehigh
Steps
- Scroll down until the last row of thumbnails is visible
- Open photo 9
- Swipe to photo 11
- Close with the X
Expected result
The grid is at the same scroll position, the filter is unchanged and focus (or the return animation) is on thumbnail 11.
TC-GAL-003Last photo: no empty slideedgemedium
Steps
- Open the last photo
- Swipe left
- Tap Next (if shown), press the Right arrow key
Expected result
The last photo stays (with a small bounce), Next is disabled, and the counter stays “12 of 12”. If the viewer loops, photo 1 appears seamlessly with “1 of 12”.
TC-GAL-004Zoom limits and resetedgemedium
Steps
- Open the floor plan
- Pinch to zoom in as far as possible
- Drag to each edge
- Go to the next photo
Expected result
Zoom stops at the maximum, the floor plan text is readable, panning stops at the photo edges, and the next photo opens at fit-to-screen.
TC-GAL-005Grid loads thumbnails, not originalspositivemedium
Preconditions
- DevTools › Network, filter “Img”, cache disabled (or a proxy on mobile)
Steps
- Open the listing
- Do not scroll
- Note the image requests and total size
- Scroll to the end of the grid
Expected result
Only visible thumbnails load at first, each a small file (well under 100 KB). The rest load while scrolling. No 3–5 MB originals.
TC-GAL-006System Back closes only the viewerpositivemedium
Preconditions
- Android phone, or a browser
Steps
- Open a photo in the viewer
- Use the Back gesture / browser Back
Expected result
The viewer closes and the listing stays open at the same place. A second Back leaves the listing.
TC-GAL-007Keyboard-only use of the viewer (web)positivehigh
Steps
- Tab to a thumbnail and press Enter
- Press Tab repeatedly
- Use Left/Right arrows
- Press Esc
Expected result
Focus moves into the viewer and cycles only through its controls, arrows change the photo, Esc closes it and focus returns to the thumbnail of the photo last shown.
TC-GAL-008Viewer on a slow networkedgelow
Preconditions
- DevTools throttling “3G” or Network Link Conditioner “3G”
Steps
- Open the gallery
- Open a photo that has not been viewed yet
- Swipe to the next one
Expected result
The grid shows placeholders of the right size, the viewer shows a blurred preview with a loading indicator, then the sharp photo. Nothing jumps or stays blank.
Tip: click a test case to expand it.
Tools
- Real devices: pinch, double-tap, swipe-down and edge gestures only feel right with real fingers. See Gestures.
- DevTools › Network (filter Img, Disable cache) to count requests and MB; the Size column and the image preview show each file’s real pixel size. Lighthouse flags “Properly size images” and “Defer offscreen images”. See Performance basics.
- Proxy tools (Charles, Proxyman) to see what a mobile app downloads, and to slow it down. See Proxy tools.
- DevTools › Rendering: emulate
prefers-reduced-motion. Elements / Accessibility pane: check the dialog role, labels and where focus goes. - Screen readers: VoiceOver (iOS/macOS), TalkBack (Android), NVDA (Windows).
Accessibility
- Thumbnails are buttons or links with a name from the alt textAlt textA text description of an image that screen readers read aloud.Open in glossary → (and “video, 0:42” for videos). A grid of unlabeled images is a list of “image, image, image” for screen reader users.
- The viewer is a modal dialog with a label (“Photos of Bright 2-room flat”). Focus moves into it on open, stays inside it (a focus trapFocus trapKeeping keyboard focus inside an open modal so users can't tab into the hidden page behind it.Open in glossary →), the page behind is hidden from assistive technology, and focus returns to the right thumbnail on close (WCAG 2.4.3 Focus Order, Level A).
Dev note
role="dialog"witharia-modal="true", or a native<dialog>opened withshowModal(); the page behind getsinert. - Keyboard (WCAG 2.1.1 Keyboard, Level A): Tab reaches Close, Previous, Next, Share and zoom; Left/Right change the photo; Esc closes. Focus is always visible.
- Gesture alternatives: swiping and pinching are path-based and multi-point gestures, so every action needs a simple tap or click alternative: Previous/Next buttons, an X button, zoom buttons or double-tap (WCAG 2.5.1 Pointer Gestures, Level A). Panning a zoomed photo by dragging needs a non-drag alternative too, for example zooming at a tapped point or zoom buttons (WCAG 2.5.7 Dragging Movements, Level AA).
- Announce changes: when the photo changes, screen readers hear its name and position (“Balcony view, photo 8 of 12”), using a polite live region or by moving focus.
- Don’t block browser zoom. Blocking it (to make the custom pinch work) stops low-vision users from zooming the rest of the page (WCAG 1.4.4 Resize Text, Level AA).
Dev note
Zoom is blocked by a viewport withuser-scalable=noormaximum-scale=1. - Motion: respect reduced motionReduced motionThe OS setting, and the prefers-reduced-motion media query, that ask apps to minimize animation.Open in glossary →. Videos with sound must not start by themselves (WCAG 1.4.2 Audio Control, Level A).
- Contrast: white buttons and captions over photos need a dark background or scrimScrimThe dimmed layer behind a modal or bottom sheet that blocks the page underneath.Open in glossary →: 4.5:1 for normal text, 3:1 for large text and UI parts (WCAG 1.4.3 / 1.4.11).
Mobile vs web
- Apps build their own viewer, or show files with Quick Look, which brings zoom and the share button.
Dev note
A pagingUIScrollView/UICollectionView(zoom viaminimumZoomScaleandmaximumZoomScale), SwiftUI, orQLPreviewController. - Users expect swipe down to close and the photo shrinking back into its thumbnail. Test that it lands on the right thumbnail after swiping to another photo.
- Test the left-edge swipe: in a viewer it may trigger the navigation Back gesture instead of the previous photo.
- Saving needs the “add photos” permission text.
Dev note
NSPhotoLibraryAddUsageDescription. - Test VoiceOver, Reduce Motion and rotation while zoomed.
- No standard viewer in Material 3: apps use a pager with a zoomable image, often from a library.
Dev note
ViewPager2, ComposeHorizontalPager; Coil or Glide for loading and caching thumbnails. - System Back (button or gesture) must close the viewer first. With predictive back (Android 14 and later), the viewer may shrink while you swipe; cancelling the gesture must leave it open.
- Test rotation and other configuration changesConfiguration changeAn Android event like rotation, folding, dark mode, font size or language that recreates the screen by default.Open in glossary →: same photo, same filter after rotating? Also test process death (does the app come back on the same photo?) and TalkBack.
- Libraries (PhotoSwipe, GLightbox, Fancybox) or a native
<dialog>. Checksrcset/sizes,loading="lazy"andwidth/heighton thumbnails in DevTools. - Browser Back should close the viewer (the page pushes a history entry), and a URL per photo (
#photo-7or?photo=7) lets users share and reload the same photo. - Test with mouse only (drag, double-click, arrows), keyboard only and touch (phone or DevTools device mode); trackpad pinch on a laptop may zoom the whole page instead.
- Check the page scroll is locked while the viewer is open (scroll lockScroll lockStopping the page behind a modal, sheet or drawer from scrolling while it's open.Open in glossary →), and that it’s restored exactly on close.
Check your understanding
Quick quiz
0/6 answered · 0 correct
1.The grid of 12 small thumbnails downloads 40 MB on a phone. What is the most likely cause?
2.You filter the gallery to “Outside” and tap the balcony photo. The living room opens. What is going on?
3.You were viewing photo 7 (opened from thumbnail 3). You close the viewer. Where should focus go?
4.Which one is NOT a correct way to close a photo viewer?
5.A viewer only supports pinch to zoom and swipe to change photos. Which WCAG criterion does it fail?
6.You zoom into photo 3 and tap Next. Photo 4 opens zoomed into a corner. Is that OK?
Key takeaways
- A gallery is two parts: a thumbnail grid (small, cropped previews) and a viewer (whole, sharp photo in a modal dialog).
- Load smart: small sized thumbnails, lazy-loaded; the large version only when opened, with the next one preloaded. Check the MB in the network log.
- The right photo, every time: test tapping thumbnails after filtering or sorting, the counter (1-based, right total) and the ends (no empty slide).
- Zoom has limits, panning stops at the edges, and zoom resets on the next photo.
- Closing (X, Esc, swipe down, Back) returns to the same scroll position and the thumbnail of the last viewed photo.
- Accessibility: thumbnails named by alt text, viewer as a dialog with a focus trap, arrow keys, and button alternatives to swipe, pinch and drag (WCAG 2.5.1, 2.5.7).
Finished this page?Nice work, this page is complete!
Mark it as done to track your progress. It's saved in this browser, no account needed.
Its review cards are now in Bites ⚡