Website Photo Gallery Best Practices
Build a fast, accessible photo gallery with useful alt text, responsive images, clear navigation, and reliable loading patterns.
A good website photo gallery helps people find, understand, and enjoy each image on any screen. Use meaningful alternatives, preserve image proportions, serve an appropriate resource for each viewport, reserve layout space, defer below-the-fold images, and keep navigation predictable and operable by keyboard and assistive technology.
1. Start with the reader’s task
Choose the gallery pattern from what visitors need to do:
| Reader task | Suitable pattern | Implementation considerations |
|---|---|---|
| Scan many related photos | Grid or masonry layout | Keep cards consistent, expose titles or captions where useful, and make every image link or button clearly labeled. |
| Study one image at a time | Grid with a lightbox or detail page | Provide close, previous, next, and close controls; return focus to the triggering item. |
| Follow a fixed sequence | Carousel | Use visible previous/next controls, keyboard support, a position indicator, and pause/stop controls for automatic movement. |
| Compare images | Stable grid or split view | Keep dimensions and captions aligned so comparisons do not jump as images load. |
There is no universally best layout. Evaluate each option by discoverability, mobile use, keyboard operation, assistive-technology output, and loading cost.
2. Write purpose-appropriate alt text
W3C’s guidance is direct: “Images must have text alternatives that describe the information or function represented by them.” Classify every image by purpose before writing its alternative.
- Informative: describe the essential subject or information in context, briefly.
- Decorative: use
alt=""when the image adds no information. - Functional: describe the action or destination when the image is a link or button, such as “Open the mountain trail photo,” rather than “mountain photo.”
Do not repeat visible captions or add keyword lists. If a detailed description is necessary, keep the alt text concise and place the longer explanation in nearby text or an expandable description.
3. Make the layout responsive
Images should fit their container without distortion as the viewport narrows or the user zooms. W3C Technique C37 describes a CSS approach using a fluid width and an automatic height; it is an advisory technique, so choose the implementation that fits your layout.
<style>
.gallery {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(14rem, 1fr));
gap: 1rem;
}
.gallery figure {
margin: 0;
}
.gallery img {
display: block;
width: 100%;
height: auto;
aspect-ratio: 4 / 3;
object-fit: cover;
border-radius: .5rem;
}
</style>
Use object-fit: cover only when cropping is acceptable. Use contain or the image’s natural ratio when the whole photograph must remain visible. Keep navigation labels and controls consistent across breakpoints. W3C’s design tips also recommend layouts that adapt to the viewport and navigation that remains clear.
4. Serve the right image resource
Do not send a desktop-sized original to every device. Generate several candidates and let the browser choose with srcset and sizes. Use modern formats when your pipeline supports them, with a fallback where needed. The recommendations below are implementation guidance, not a universal quality setting or file-size target.
<picture>
<source
type="image/avif"
srcset="/photos/lake-480.avif 480w,
/photos/lake-960.avif 960w,
/photos/lake-1600.avif 1600w"
sizes="(max-width: 700px) 100vw, (max-width: 1100px) 50vw, 33vw">
<source
type="image/webp"
srcset="/photos/lake-480.webp 480w,
/photos/lake-960.webp 960w,
/photos/lake-1600.webp 1600w"
sizes="(max-width: 700px) 100vw, (max-width: 1100px) 50vw, 33vw">
<img
src="/photos/lake-960.jpg"
srcset="/photos/lake-480.jpg 480w,
/photos/lake-960.jpg 960w,
/photos/lake-1600.jpg 1600w"
sizes="(max-width: 700px) 100vw, (max-width: 1100px) 50vw, 33vw"
width="1600" height="1200"
loading="lazy" decoding="async"
alt="Kayakers crossing a calm lake beneath pine-covered hills">
</picture>
The intrinsic width and height reserve space and reduce layout movement. Keep the prominent image already visible on initial load eager; lazy-load images that begin below the fold. web.dev’s responsive-image guidance also covers fetch priority for important images. Google Search Central documents responsive image and image-accessibility practices.
5. Load images at the right time
- Use
loading="lazy"for gallery items below the initial viewport. - Leave the first prominent image eager; indiscriminate lazy loading can delay it.
- Use
decoding="async"for non-critical images when it fits your rendering strategy. - Set dimensions or an
aspect-ratioon every tile before the file arrives. - Test on a narrow screen and a slow connection. A gallery that looks fine on a fast desktop can still delay the main content on mobile.
6. Make navigation accessible
Give every interactive image a visible focus style and an accessible name. A lightbox should trap focus while open, close with Escape, provide labeled previous/next buttons, and return focus to the opener. Ensure the enlarged image has an appropriate alternative and that captions are available to screen readers.
For carousels, avoid automatic movement when possible. If slides advance automatically, provide pause, stop, or hide controls. Movement that continues for more than five seconds can create an accessibility failure pattern described by WebAIM in its accessible-images guidance. Do not rely on color alone for the current slide; include a position such as “3 of 12.”
7. A complete, framework-free gallery example
The following example combines responsive candidates, reserved space, keyboard-friendly links, captions, and lazy loading. Replace the paths and text with content from your own collection.
<main>
<h1>Coastal walk photographs</h1>
<section class="gallery" aria-label="Coastal walk photographs">
<figure>
<a href="/photos/cliffs-large.jpg">
<img
src="/photos/cliffs-800.jpg"
srcset="/photos/cliffs-400.jpg 400w,
/photos/cliffs-800.jpg 800w,
/photos/cliffs-1400.jpg 1400w"
sizes="(max-width: 700px) 100vw, (max-width: 1100px) 50vw, 33vw"
width="1400" height="1050"
loading="eager"
fetchpriority="high"
alt="Waves below red cliffs on the coastal walking trail">
</a>
<figcaption>Red cliffs at low tide</figcaption>
</figure>
<figure>
<a href="/photos/lighthouse-large.jpg">
<img
src="/photos/lighthouse-800.jpg"
srcset="/photos/lighthouse-400.jpg 400w,
/photos/lighthouse-800.jpg 800w,
/photos/lighthouse-1400.jpg 1400w"
sizes="(max-width: 700px) 100vw, (max-width: 1100px) 50vw, 33vw"
width="1400" height="1050"
loading="lazy" decoding="async"
alt="White lighthouse beside a grassy headland at sunset">
</a>
<figcaption>The headland lighthouse</figcaption>
</figure>
</section>
</main>
8. Test checklist
- Every informative image has concise, contextual alt text.
- Decorative images use an empty alt value.
- Linked images describe their destination or action.
- Tiles preserve their ratio before images load.
- Small screens receive smaller candidates through
srcsetandsizes. - Only below-the-fold images are lazy-loaded.
- Keyboard users can reach, operate, and close every control.
- Focus remains visible and returns to the triggering item after a lightbox closes.
- Automatic movement can be paused or stopped.
- Captions and image order still make sense when CSS or JavaScript is unavailable.
9. Troubleshooting common gallery problems
| Symptom | Likely cause | Fix |
|---|---|---|
| Images jump while loading | No intrinsic dimensions or ratio | Add width and height, or set a matching aspect-ratio. |
| Mobile users download huge files | Missing or incorrect srcset/sizes |
Generate width candidates and make sizes match the rendered column width. |
| The first image appears late | Everything is lazy-loaded | Make the prominent image eager and consider fetch priority. |
| Photos look stretched | Forced width and height without preserved ratio | Use height: auto or a deliberate crop with object-fit. |
| Screen readers announce meaningless links | Alt text describes appearance instead of function | Name the destination or action, and avoid duplicate visible captions. |
| Keyboard users get trapped or lose context | Incomplete lightbox focus handling | Move focus into the dialog, support Escape, label controls, and restore focus on close. |
| Carousel content is missed | Uncontrolled automatic movement | Provide pause/stop controls and clear previous/next buttons; avoid auto-advance when it is not needed. |
| Images fail intermittently | Broken paths, restrictive caching, or an origin timeout | Check the final URL in a new request, inspect response status and content type, and add a fallback image or retry policy at the delivery layer. |
10. Performance, reliability, and cost notes
- Performance: the biggest wins usually come from choosing the right candidate, reserving layout space, and avoiding eager downloads below the fold.
- Reliability: keep image URLs stable, monitor failed requests, and retain an accessible HTML structure if JavaScript fails.
- Cost: responsive derivatives reduce transfer and storage per visit, while a CDN or image optimization service can handle transformation and caching. Verify the actual limits and pricing of any vendor before choosing it.
- Content operations: store the focal subject, caption, credit, dimensions, and alt text with each asset so editors do not have to reconstruct context later.
11. Or skip the browser setup
If you need screenshots of gallery pages for documentation, previews, regression checks, or an AI workflow, ScreenshotNeo returns a PNG, JPEG, WebP, or PDF from one GET request. It accepts the cookie or consent banner like a visitor, then removes more than 60 known consent platforms, newsletter popups, and chat widgets before capture; each step can be turned off. Bot checks, CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed, and the response reports the result in X-Page-Verdict and X-Billed headers.
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://example.com/gallery -o gallery.webp
import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://example.com/gallery"}, timeout=90)
open("gallery.webp", "wb").write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://example.com/gallery' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
See the ScreenshotNeo API documentation for the 63 options, including full-page capture with lazy images loaded, CSS-element capture, dark mode, device presets, custom viewport and retina scale, custom CSS and JavaScript, waits, request blocking, headers and cookies, timezone and geolocation, resizing, caching, signed links, asynchronous jobs, webhooks, bulk capture, usage data, and the OpenAPI specification. Its MCP server exposes take_screenshot, get_page_info, and capture_pdf for Claude, Cursor, and other MCP clients. The free plan includes 1,000 screenshots each month without a card; paid plans start at $5 for 3,000.
Create a free ScreenshotNeo account and get 1,000 screenshots a month with no card.
12. FAQ
Should every gallery use a lightbox?
No. A detail page is often clearer for images that need substantial context, metadata, or sharing. Use a lightbox when quick inspection is the main task and its focus behavior is implemented correctly.
Is masonry better than a regular grid?
Neither is universally better. Masonry can use varied aspect ratios efficiently, while a regular grid makes scanning and comparison more predictable. Test both against your content and interaction needs.
Can I use the same alt text as the caption?
Only when the caption itself communicates the image’s essential information and the duplication does not create unnecessary repetition. Functional images still need an alternative that names the action or destination.
How many responsive image sizes should I create?
Create candidates that cover the actual rendered widths in your layouts, including high-density screens where appropriate. Measure the gallery columns and adjust rather than choosing a fixed universal number.
When should a gallery be a separate page?
Use a separate detail page when each photograph needs a stable URL, rich description, search visibility, comments, licensing information, or a long-form reading experience.


