ACF Gallery Fields enable multiple image uploads and management through drag-and-drop interfaces returning arrays of image data for custom gallery displays. From simple grid layouts and masonry galleries to lightbox integrations and responsive image galleries, the Gallery field eliminates plugin dependencies. This comprehensive tutorial teaches Gallery field setup, image array manipulation, responsive grid layouts, lightbox library integration, and advanced techniques creating professional custom image galleries with ACF PRO.
Creating Gallery Field
Add Gallery Field via ACF UI:
- Custom Fields → Field Groups
- Add Field
- Field Type: Gallery (ACF PRO)
- Configuration:
- Field Label: Project Gallery
- Field Name: project_gallery
- Return Format: Array (recommended)
- Library: All (uploaded to post or all)
- Min/Max Images: Set limits
- Insert: Append/Prepend
Field Configuration Options:
Return Format:
- Array: Full image data (recommended)
- URL: Image URLs only
- ID: Attachment IDs only
Library:
- all: All images in media library
- uploadedTo: Only images uploaded to this post
Min/Max:
- Min: 1 (require at least one image)
- Max: 20 (limit to 20 images)
Insert:
- append: Add new images to end
- prepend: Add new images to beginning
Basic Gallery Display
Simple Gallery Grid:
<?php
$images = get_field('project_gallery');
if ($images) :
?>
<div class="image-gallery">
<?php foreach ($images as $image) : ?>
<div class="gallery-item">
<img src="<?php echo esc_url($image['sizes']['medium']); ?>"
alt="<?php echo esc_attr($image['alt']); ?>"
title="<?php echo esc_attr($image['title']); ?>" />
</div>
<?php endforeach; ?>
</div>
<?php endif; ?>CSS for Grid Layout:
.image-gallery {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
gap: 20px;
padding: 20px 0;
}
.gallery-item {
position: relative;
overflow: hidden;
border-radius: 8px;
}
.gallery-item img {
width: 100%;
height: 250px;
object-fit: cover;
transition: transform 0.3s ease;
}
.gallery-item:hover img {
transform: scale(1.05);
}Gallery with Captions
Display Images with Captions:
<?php
$images = get_field('project_gallery');
if ($images) :
?>
<div class="gallery-with-captions">
<?php foreach ($images as $image) : ?>
<figure class="gallery-item">
<img src="<?php echo esc_url($image['sizes']['large']); ?>"
alt="<?php echo esc_attr($image['alt']); ?>" />
<?php if ($image['caption']) : ?>
<figcaption>
<?php echo esc_html($image['caption']); ?>
</figcaption>
<?php endif; ?>
</figure>
<?php endforeach; ?>
</div>
<?php endif; ?>Caption Styling:
.gallery-with-captions {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 30px;
}
.gallery-item figcaption {
padding: 10px;
background: #f8f9fa;
font-size: 14px;
color: #6c757d;
text-align: center;
}Lightbox Integration
GLightbox Integration:
Enqueue GLightbox (functions.php):
function mytheme_gallery_lightbox() {
// GLightbox CSS
wp_enqueue_style(
'glightbox',
'https://cdn.jsdelivr.net/npm/glightbox/dist/css/glightbox.min.css',
array(),
'3.2.0'
);
// GLightbox JS
wp_enqueue_script(
'glightbox',
'https://cdn.jsdelivr.net/npm/glightbox/dist/js/glightbox.min.js',
array(),
'3.2.0',
true
);
// Initialize lightbox
wp_add_inline_script('glightbox', '
document.addEventListener("DOMContentLoaded", function() {
GLightbox({
selector: ".glightbox"
});
});
');
}
add_action('wp_enqueue_scripts', 'mytheme_gallery_lightbox');Gallery Template with Lightbox:
<?php
$images = get_field('project_gallery');
if ($images) :
?>
<div class="lightbox-gallery">
<?php foreach ($images as $image) : ?>
<a href="<?php echo esc_url($image['url']); ?>"
class="glightbox gallery-item"
data-gallery="gallery1"
data-description="<?php echo esc_attr($image['caption']); ?>">
<img src="<?php echo esc_url($image['sizes']['medium']); ?>"
alt="<?php echo esc_attr($image['alt']); ?>" />
</a>
<?php endforeach; ?>
</div>
<?php endif; ?>Masonry Gallery Layout
Masonry Grid Gallery:
<?php
$images = get_field('project_gallery');
if ($images) :
?>
<div class="masonry-gallery">
<?php foreach ($images as $index => $image) :
// Vary heights for masonry effect
$tall = ($index % 3 === 0) ? 'tall' : '';
?>
<div class="gallery-item <?php echo esc_attr($tall); ?>">
<a href="<?php echo esc_url($image['url']); ?>"
class="glightbox"
data-gallery="masonry">
<img src="<?php echo esc_url($image['sizes']['large']); ?>"
alt="<?php echo esc_attr($image['alt']); ?>" />
</a>
</div>
<?php endforeach; ?>
</div>
<?php endif; ?>Masonry CSS:
.masonry-gallery {
column-count: 3;
column-gap: 15px;
}
.gallery-item {
break-inside: avoid;
margin-bottom: 15px;
}
.gallery-item img {
width: 100%;
display: block;
border-radius: 8px;
}
.gallery-item.tall {
height: 400px;
}
@media (max-width: 768px) {
.masonry-gallery {
column-count: 2;
}
}
@media (max-width: 480px) {
.masonry-gallery {
column-count: 1;
}
}Slider Gallery
Image Slider with Navigation:
<?php
$images = get_field('project_gallery');
if ($images) :
?>
<div class="slider-gallery">
<div class="slider-container">
<?php foreach ($images as $index => $image) : ?>
<div class="slide <?php echo ($index === 0) ? 'active' : ''; ?>"
data-slide="<?php echo esc_attr($index); ?>">
<img src="<?php echo esc_url($image['sizes']['large']); ?>"
alt="<?php echo esc_attr($image['alt']); ?>" />
<?php if ($image['caption']) : ?>
<div class="slide-caption">
<?php echo esc_html($image['caption']); ?>
</div>
<?php endif; ?>
</div>
<?php endforeach; ?>
</div>
<button class="slider-prev" aria-label="Previous image">‹</button>
<button class="slider-next" aria-label="Next image">›</button>
<div class="slider-dots">
<?php foreach ($images as $index => $image) : ?>
<button class="dot <?php echo ($index === 0) ? 'active' : ''; ?>"
data-slide="<?php echo esc_attr($index); ?>"
aria-label="Go to slide <?php echo ($index + 1); ?>"></button>
<?php endforeach; ?>
</div>
</div>
<?php endif; ?>Slider JavaScript:
document.addEventListener("DOMContentLoaded", function () {
const slides = document.querySelectorAll(".slide");
const dots = document.querySelectorAll(".dot");
const prevBtn = document.querySelector(".slider-prev");
const nextBtn = document.querySelector(".slider-next");
let currentSlide = 0;
function showSlide(n) {
slides.forEach((slide) => slide.classList.remove("active"));
dots.forEach((dot) => dot.classList.remove("active"));
if (n >= slides.length) currentSlide = 0;
if (n < 0) currentSlide = slides.length - 1;
slides[currentSlide].classList.add("active");
dots[currentSlide].classList.add("active");
}
prevBtn.addEventListener("click", () => {
currentSlide--;
showSlide(currentSlide);
});
nextBtn.addEventListener("click", () => {
currentSlide++;
showSlide(currentSlide);
});
dots.forEach((dot, index) => {
dot.addEventListener("click", () => {
currentSlide = index;
showSlide(currentSlide);
});
});
});Responsive Image Sizes
Use Appropriate Image Sizes:
<?php
$images = get_field('project_gallery');
if ($images) :
?>
<div class="responsive-gallery">
<?php foreach ($images as $image) : ?>
<div class="gallery-item">
<picture>
<!-- Mobile: thumbnail -->
<source media="(max-width: 480px)"
srcset="<?php echo esc_url($image['sizes']['thumbnail']); ?>">
<!-- Tablet: medium -->
<source media="(max-width: 768px)"
srcset="<?php echo esc_url($image['sizes']['medium']); ?>">
<!-- Desktop: large -->
<img src="<?php echo esc_url($image['sizes']['large']); ?>"
alt="<?php echo esc_attr($image['alt']); ?>" />
</picture>
</div>
<?php endforeach; ?>
</div>
<?php endif; ?>Gallery with Thumbnails
Main Image with Thumbnail Navigation:
<?php
$images = get_field('project_gallery');
if ($images) :
?>
<div class="gallery-with-thumbs">
<!-- Main Display -->
<div class="main-image">
<img id="main-gallery-img"
src="<?php echo esc_url($images[0]['sizes']['large']); ?>"
alt="<?php echo esc_attr($images[0]['alt']); ?>" />
</div>
<!-- Thumbnails -->
<div class="thumbnail-strip">
<?php foreach ($images as $index => $image) : ?>
<button class="thumb <?php echo ($index === 0) ? 'active' : ''; ?>"
data-full="<?php echo esc_url($image['sizes']['large']); ?>"
data-alt="<?php echo esc_attr($image['alt']); ?>">
<img src="<?php echo esc_url($image['sizes']['thumbnail']); ?>"
alt="<?php echo esc_attr($image['alt']); ?>" />
</button>
<?php endforeach; ?>
</div>
</div>
<?php endif; ?>Thumbnail Navigation JavaScript:
document.querySelectorAll(".thumb").forEach(function (thumb) {
thumb.addEventListener("click", function () {
const fullSrc = this.getAttribute("data-full");
const altText = this.getAttribute("data-alt");
const mainImg = document.getElementById("main-gallery-img");
mainImg.src = fullSrc;
mainImg.alt = altText;
document.querySelectorAll(".thumb").forEach((t) => t.classList.remove("active"));
this.classList.add("active");
});
});Lazy Loading Gallery
Performance Optimization:
<?php
$images = get_field('project_gallery');
if ($images) :
?>
<div class="lazy-gallery">
<?php foreach ($images as $index => $image) :
// Load first 3 images immediately, lazy load rest
$loading = ($index < 3) ? 'eager' : 'lazy';
?>
<div class="gallery-item">
<img src="<?php echo esc_url($image['sizes']['medium']); ?>"
alt="<?php echo esc_attr($image['alt']); ?>"
loading="<?php echo esc_attr($loading); ?>"
width="<?php echo esc_attr($image['sizes']['medium-width']); ?>"
height="<?php echo esc_attr($image['sizes']['medium-height']); ?>" />
</div>
<?php endforeach; ?>
</div>
<?php endif; ?>Filter Gallery by Category
Gallery with Category Filters:
<?php
$images = get_field('project_gallery');
if ($images) :
?>
<div class="filtered-gallery">
<!-- Category Filters -->
<div class="gallery-filters">
<button class="filter-btn active" data-filter="all">All</button>
<button class="filter-btn" data-filter="product">Product</button>
<button class="filter-btn" data-filter="lifestyle">Lifestyle</button>
<button class="filter-btn" data-filter="detail">Detail</button>
</div>
<!-- Gallery Items -->
<div class="gallery-grid">
<?php foreach ($images as $image) :
// Get category from image description
$category = $image['description'] ?: 'all';
?>
<div class="gallery-item" data-category="<?php echo esc_attr($category); ?>">
<img src="<?php echo esc_url($image['sizes']['medium']); ?>"
alt="<?php echo esc_attr($image['alt']); ?>" />
</div>
<?php endforeach; ?>
</div>
</div>
<?php endif; ?>Filter JavaScript:
document.querySelectorAll(".filter-btn").forEach(function (btn) {
btn.addEventListener("click", function () {
const filter = this.getAttribute("data-filter");
document.querySelectorAll(".filter-btn").forEach((b) => b.classList.remove("active"));
this.classList.add("active");
document.querySelectorAll(".gallery-item").forEach(function (item) {
const category = item.getAttribute("data-category");
if (filter === "all" || category === filter) {
item.style.display = "block";
} else {
item.style.display = "none";
}
});
});
});Gallery Image Count
Display Image Count:
<?php
$images = get_field('project_gallery');
if ($images) :
$image_count = count($images);
?>
<div class="gallery-section">
<div class="gallery-header">
<h2>Project Gallery</h2>
<p class="image-count">
<?php echo esc_html($image_count); ?>
<?php echo ($image_count === 1) ? 'Image' : 'Images'; ?>
</p>
</div>
<div class="image-gallery">
<?php foreach ($images as $image) : ?>
<div class="gallery-item">
<img src="<?php echo esc_url($image['sizes']['medium']); ?>"
alt="<?php echo esc_attr($image['alt']); ?>" />
</div>
<?php endforeach; ?>
</div>
</div>
<?php endif; ?>Accessing Image Metadata
Full Image Data Available:
<?php
$images = get_field('project_gallery');
if ($images) :
foreach ($images as $image) :
// Available image data:
$id = $image['ID'];
$title = $image['title'];
$alt = $image['alt'];
$caption = $image['caption'];
$description = $image['description'];
$url = $image['url'];
$width = $image['width'];
$height = $image['height'];
// Image sizes
$thumbnail = $image['sizes']['thumbnail'];
$medium = $image['sizes']['medium'];
$large = $image['sizes']['large'];
$full = $image['sizes']['full'];
// Size dimensions
$thumb_width = $image['sizes']['thumbnail-width'];
$thumb_height = $image['sizes']['thumbnail-height'];
endforeach;
endif;
?>Programmatic Gallery Registration
Register Gallery Field via PHP:
function mytheme_register_gallery_field() {
if (function_exists('acf_add_local_field_group')) {
acf_add_local_field_group(array(
'key' => 'group_project_gallery',
'title' => 'Project Gallery',
'fields' => array(
array(
'key' => 'field_project_gallery',
'label' => 'Gallery Images',
'name' => 'project_gallery',
'type' => 'gallery',
'return_format' => 'array',
'library' => 'all',
'min' => 1,
'max' => 50,
'insert' => 'append',
),
),
'location' => array(
array(
array(
'param' => 'post_type',
'operator' => '==',
'value' => 'portfolio',
),
),
),
));
}
}
add_action('acf/init', 'mytheme_register_gallery_field');Best Practices
Always Check for Images:
<?php
$images = get_field('project_gallery');
// Good - check before loop
if ($images) {
foreach ($images as $image) {
// Display images
}
}
// Bad - no check, may cause errors
foreach (get_field('project_gallery') as $image) {
// Display images
}
?>Optimize Image Sizes:
// Use appropriate size for context
$thumbnail = $image['sizes']['thumbnail']; // 150x150
$medium = $image['sizes']['medium']; // 300x300
$large = $image['sizes']['large']; // 1024x1024Escape All Output:
<?php echo esc_url($image['url']); ?>
<?php echo esc_attr($image['alt']); ?>
<?php echo esc_html($image['caption']); ?>Conclusion
ACF Gallery Fields enable custom image galleries through drag-and-drop interfaces returning comprehensive image data arrays for flexible display options. Implement responsive grid layouts with CSS Grid, integrate lightbox libraries like GLightbox for full-screen viewing, create masonry galleries with column-count, build image sliders with JavaScript navigation, and optimize performance with lazy loading attributes. Gallery fields eliminate gallery plugin dependencies while providing complete control over gallery styling, functionality, and responsive behavior.
External Links
- ACF Gallery Field Documentation
- GLightbox Library
- CSS Grid Layout Guide
- Native Lazy Loading
- Responsive Images
Call to Action
Gallery configurations need backup protection. Backup Copilot Pro backs up your WordPress gallery field settings and image data automatically. Safeguard your custom galleries—start your free 30-day trial today!

