A WordPress slider with three images is an effective way to display products, portfolio projects, testimonials, blog posts, team members, or photographs without using too much page space.

In this tutorial, you will create a responsive multiple-image slider using HTML, CSS, and vanilla JavaScript. The carousel displays three images at a time on desktop screens, two images on tablets, and one image on mobile devices.

You can test the complete slider in CodePen before adding it to your WordPress website. The finished slider includes previous and next buttons, navigation dots, autoplay controls, swipe gestures, keyboard navigation, responsive breakpoints, and support for multiple sliders on the same page.

No external JavaScript carousel library is required.

How Do You Create a WordPress Slider With Three Images?

To create a WordPress slider with three images, place the images inside a horizontal flexbox track, set each slide to occupy one-third of the visible area on desktop, and use JavaScript to move the track when visitors select the previous or next controls.

Use responsive CSS breakpoints to change the number of visible images:

DeviceImages displayed
Desktop3
Tablet2
Mobile1

The HTML creates the carousel structure, CSS controls the layout and animation, and JavaScript manages navigation, autoplay, swipe gestures, responsive calculations, and accessibility states.

Subscribe to our Newsletter

Stay updated with our latest news and offers.
Thanks for signing up!

What This Three-Image Slider Includes

The slider in this tutorial provides:

  • Three images per view on desktop
  • Two images per view on tablets
  • One image per view on mobile
  • Previous and next navigation
  • Navigation dots
  • Optional autoplay
  • Autoplay pause and start control
  • Pause on mouse hover
  • Pause during keyboard interaction
  • Left and right arrow-key navigation
  • Touch and swipe support
  • Reduced-motion support
  • Multiple-slider support
  • Descriptive image captions
  • Accessible buttons and slide labels
  • No jQuery or external carousel library

Is This a Slider or a Carousel?

The terms “slider” and “carousel” are often used interchangeably, but they can describe slightly different layouts.

A traditional image slider commonly displays one large image at a time. A carousel can display several items within the same visible area and move them horizontally.

Because the example in this tutorial displays three images simultaneously, it is technically a multiple-item carousel. However, many WordPress users search for this feature using phrases such as:

  • WordPress slider with three images
  • Three-image slider
  • Image slider with three images at a time
  • JavaScript slider with multiple items
  • WordPress carousel without a plugin

This tutorial uses both “slider” and “carousel” to make the instructions easier to understand.

When Should You Use a Three-Image Slider?

A responsive three-image slider works well when visitors need to browse several related items without leaving the current section.

Product collections

Show featured products, product categories, new arrivals, or recommended items within an online store.

Photography portfolios

Present landscapes, event photographs, design projects, or creative work while keeping the page organized.

Blog post carousels

Display recent posts, related articles, popular resources, or category-based content.

Team member profiles

Show employee photographs, roles, names, and short descriptions in a compact layout.

Customer testimonials

Present customer stories or reviews as individual cards.

Service listings

Display several services with an image, heading, description, and call-to-action button.

Property galleries

Show houses, apartments, interiors, or commercial properties in a responsive carousel.

Three-Image Carousel Features at a Glance

FeatureIncluded
Three desktop imagesYes
Tablet layoutTwo images
Mobile layoutOne image
Previous and next controlsYes
Navigation dotsYes
AutoplayOptional
Pause autoplayYes
Swipe navigationYes
Keyboard navigationYes
Multiple sliders per pageYes
External library requiredNo
WordPress compatibleYes

Complete WP Slider With Three Images CodePen Tutorial

CodePen separates front-end code into HTML, CSS, and JavaScript panels. Paste each of the following code sections into its matching panel.

The example contains six images. Six or more slides are recommended because displaying only three images in a three-items-per-view layout would leave nothing additional to reveal on desktop.

Step 1: Add the Slider HTML

Paste the following code into the HTML panel in CodePen:

<section
  class="three-image-carousel"
  aria-label="Featured image gallery"
  data-autoplay="true"
  data-interval="4500"
>
  <div
    class="carousel__viewport"
    tabindex="0"
    aria-label="Use the left and right arrow keys to navigate"
  >
    <ul class="carousel__track">
      <li class="carousel__slide">
        <figure>
          <img
            src="https://picsum.photos/id/1015/900/600"
            alt="Sample landscape image showing mountains and water"
            width="900"
            height="600"
            decoding="async"
          >
          <figcaption>Mountain Landscape</figcaption>
        </figure>
      </li>

      <li class="carousel__slide">
        <figure>
          <img
            src="https://picsum.photos/id/1016/900/600"
            alt="Sample outdoor landscape photograph"
            width="900"
            height="600"
            decoding="async"
          >
          <figcaption>Outdoor Adventure</figcaption>
        </figure>
      </li>

      <li class="carousel__slide">
        <figure>
          <img
            src="https://picsum.photos/id/1025/900/600"
            alt="Sample animal photograph"
            width="900"
            height="600"
            decoding="async"
          >
          <figcaption>Wildlife Photography</figcaption>
        </figure>
      </li>

      <li class="carousel__slide">
        <figure>
          <img
            src="https://picsum.photos/id/1035/900/600"
            alt="Sample scenic nature photograph"
            width="900"
            height="600"
            loading="lazy"
            decoding="async"
          >
          <figcaption>Natural Scenery</figcaption>
        </figure>
      </li>

      <li class="carousel__slide">
        <figure>
          <img
            src="https://picsum.photos/id/1043/900/600"
            alt="Sample landscape with a calm natural setting"
            width="900"
            height="600"
            loading="lazy"
            decoding="async"
          >
          <figcaption>Peaceful View</figcaption>
        </figure>
      </li>

      <li class="carousel__slide">
        <figure>
          <img
            src="https://picsum.photos/id/1050/900/600"
            alt="Sample travel and nature photograph"
            width="900"
            height="600"
            loading="lazy"
            decoding="async"
          >
          <figcaption>Travel Photography</figcaption>
        </figure>
      </li>
    </ul>

    <button
      class="carousel__button carousel__button--prev"
      type="button"
      aria-label="Show previous images"
    >
      &#10094;
    </button>

    <button
      class="carousel__button carousel__button--next"
      type="button"
      aria-label="Show next images"
    >
      &#10095;
    </button>
  </div>

  <div class="carousel__footer">
    <div
      class="carousel__dots"
      aria-label="Choose which images to display"
    ></div>

    <button
      class="carousel__toggle"
      type="button"
      aria-pressed="true"
    >
      Pause autoplay
    </button>
  </div>

  <p class="carousel__status visually-hidden" aria-live="off"></p>
</section>

Understanding the HTML structure

The main <section> contains the complete carousel. Its aria-label gives the component an accessible name.

The important elements are:

ElementPurpose
.three-image-carouselContains one complete carousel
.carousel__viewportHides slides outside the visible area
.carousel__trackHolds all slides in one horizontal row
.carousel__slideRepresents an individual carousel item
.carousel__button--prevMoves to the previous position
.carousel__button--nextMoves to the next position
.carousel__dotsHolds dynamically generated navigation dots
.carousel__toggleStarts or pauses autoplay
.carousel__statusAnnounces manual slide changes to assistive technology

The data-autoplay attribute controls whether the carousel starts automatically:

data-autoplay="true"

Change it to false to disable automatic movement:

data-autoplay="false"

The data-interval value controls the delay between automatic transitions in milliseconds:

data-interval="4500"

A value of 4500 means the slider changes every 4.5 seconds.

Step 2: Style the Three-Image Slider With CSS

Paste the following code into the CSS panel:

.three-image-carousel,
.three-image-carousel * {
  box-sizing: border-box;
}

.three-image-carousel {
  --carousel-gap: 18px;
  --carousel-radius: 14px;
  --carousel-control-size: 44px;
  width: 100%;
  max-width: 1200px;
  margin: 40px auto;
  font-family: Arial, Helvetica, sans-serif;
}

.carousel__viewport {
  position: relative;
  width: 100%;
  overflow: hidden;
  border-radius: var(--carousel-radius);
  touch-action: pan-y;
  outline-offset: 5px;
}

.carousel__track {
  display: flex;
  gap: var(--carousel-gap);
  padding: 0;
  margin: 0;
  list-style: none;
  transition: transform 0.45s ease;
  will-change: transform;
}

.carousel__slide {
  flex: 0 0 100%;
  min-width: 0;
}

.carousel__slide figure {
  position: relative;
  overflow: hidden;
  height: 100%;
  margin: 0;
  border-radius: var(--carousel-radius);
  background: #f1f2f5;
}

.carousel__slide img {
  display: block;
  width: 100%;
  aspect-ratio: 3 / 2;
  height: auto;
  object-fit: cover;
  user-select: none;
  -webkit-user-drag: none;
}

.carousel__slide figcaption {
  position: absolute;
  right: 12px;
  bottom: 12px;
  left: 12px;
  padding: 10px 12px;
  border-radius: 8px;
  color: #ffffff;
  font-size: 15px;
  font-weight: 600;
  line-height: 1.4;
  background: rgba(0, 0, 0, 0.62);
}

.carousel__button {
  position: absolute;
  top: 50%;
  z-index: 2;
  display: grid;
  width: var(--carousel-control-size);
  height: var(--carousel-control-size);
  padding: 0;
  place-items: center;
  transform: translateY(-50%);
  border: 0;
  border-radius: 50%;
  color: #ffffff;
  background: rgba(0, 0, 0, 0.72);
  font-size: 22px;
  cursor: pointer;
  transition:
    background-color 0.2s ease,
    transform 0.2s ease;
}

.carousel__button:hover {
  background: rgba(0, 0, 0, 0.92);
}

.carousel__button:focus-visible,
.carousel__toggle:focus-visible,
.carousel__dot:focus-visible {
  outline: 3px solid currentColor;
  outline-offset: 3px;
}

.carousel__button:disabled {
  opacity: 0.35;
  cursor: not-allowed;
}

.carousel__button--prev {
  left: 12px;
}

.carousel__button--next {
  right: 12px;
}

.carousel__footer {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: 18px;
  margin-top: 18px;
}

.carousel__dots {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 9px;
}

.carousel__dot {
  width: 11px;
  height: 11px;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: #b8bbc3;
  cursor: pointer;
  transition:
    transform 0.2s ease,
    background-color 0.2s ease;
}

.carousel__dot:hover {
  transform: scale(1.2);
}

.carousel__dot.is-active {
  background: #222222;
  transform: scale(1.25);
}

.carousel__toggle {
  padding: 9px 14px;
  border: 1px solid #222222;
  border-radius: 7px;
  color: #222222;
  background: #ffffff;
  font-size: 14px;
  font-weight: 600;
  cursor: pointer;
}

.carousel__toggle:hover {
  color: #ffffff;
  background: #222222;
}

.visually-hidden {
  position: absolute !important;
  width: 1px !important;
  height: 1px !important;
  padding: 0 !important;
  margin: -1px !important;
  overflow: hidden !important;
  clip: rect(0, 0, 0, 0) !important;
  white-space: nowrap !important;
  border: 0 !important;
}

/* Two images on tablets */
@media (min-width: 768px) {
  .carousel__slide {
    flex-basis: calc((100% - var(--carousel-gap)) / 2);
  }
}

/* Three images on desktop */
@media (min-width: 1024px) {
  .carousel__slide {
    flex-basis:
      calc(
        (100% - var(--carousel-gap) - var(--carousel-gap)) / 3
      );
  }
}

@media (max-width: 767px) {
  .three-image-carousel {
    --carousel-gap: 12px;
    --carousel-control-size: 40px;
  }

  .carousel__button--prev {
    left: 8px;
  }

  .carousel__button--next {
    right: 8px;
  }

  .carousel__slide figcaption {
    font-size: 14px;
  }
}

@media (prefers-reduced-motion: reduce) {
  .carousel__track,
  .carousel__button,
  .carousel__dot {
    transition: none;
  }
}

How the responsive CSS works

Each slide uses flex-basis to control how much of the visible carousel area it occupies.

On mobile, every slide occupies the full width:

.carousel__slide {
  flex: 0 0 100%;
}

On tablets, the visible width is divided between two slides:

@media (min-width: 768px) {
  .carousel__slide {
    flex-basis: calc((100% - var(--carousel-gap)) / 2);
  }
}

On desktop screens, the available width is divided between three slides:

@media (min-width: 1024px) {
  .carousel__slide {
    flex-basis:
      calc(
        (100% - var(--carousel-gap) - var(--carousel-gap)) / 3
      );
  }
}

The calculation subtracts the spaces between the visible images before dividing the remaining width.

Step 3: Add the JavaScript Slider Functionality

Paste this code into the JavaScript panel:

document.addEventListener('DOMContentLoaded', () => {
  document
    .querySelectorAll('.three-image-carousel')
    .forEach(initCarousel);
});

function initCarousel(carousel) {
  const track = carousel.querySelector('.carousel__track');
  const slides = Array.from(
    carousel.querySelectorAll('.carousel__slide')
  );
  const previousButton = carousel.querySelector(
    '.carousel__button--prev'
  );
  const nextButton = carousel.querySelector(
    '.carousel__button--next'
  );
  const dotsContainer = carousel.querySelector('.carousel__dots');
  const autoplayButton = carousel.querySelector('.carousel__toggle');
  const status = carousel.querySelector('.carousel__status');

  if (
    !track ||
    slides.length === 0 ||
    !previousButton ||
    !nextButton
  ) {
    return;
  }

  let currentIndex = 0;
  let autoplayTimer = null;
  let resizeTimer = null;
  let pointerStartX = null;
  let autoplayEnabled = carousel.dataset.autoplay === 'true';

  const autoplayInterval = Math.max(
    Number.parseInt(carousel.dataset.interval, 10) || 4500,
    2000
  );

  const reducedMotion = window.matchMedia(
    '(prefers-reduced-motion: reduce)'
  );

  if (reducedMotion.matches) {
    autoplayEnabled = false;
  }

  slides.forEach((slide, index) => {
    slide.setAttribute('role', 'group');
    slide.setAttribute('aria-roledescription', 'slide');
    slide.setAttribute(
      'aria-label',
      `${index + 1} of ${slides.length}`
    );
  });

  function visibleSlides() {
    if (window.innerWidth >= 1024) {
      return 3;
    }

    if (window.innerWidth >= 768) {
      return 2;
    }

    return 1;
  }

  function maximumIndex() {
    return Math.max(0, slides.length - visibleSlides());
  }

  function slideStep() {
    const slideWidth = slides[0].getBoundingClientRect().width;
    const gap =
      Number.parseFloat(getComputedStyle(track).columnGap) || 0;

    return slideWidth + gap;
  }

  function buildDots() {
    if (!dotsContainer) {
      return;
    }

    dotsContainer.innerHTML = '';

    const totalPositions = maximumIndex() + 1;

    for (let index = 0; index < totalPositions; index += 1) {
      const dot = document.createElement('button');

      dot.type = 'button';
      dot.className = 'carousel__dot';
      dot.setAttribute(
        'aria-label',
        `Show images starting with image ${index + 1}`
      );

      dot.addEventListener('click', () => {
        currentIndex = index;
        updateCarousel(true);
        restartAutoplay();
      });

      dotsContainer.appendChild(dot);
    }
  }

  function updateCarousel(announce = false) {
    const maxIndex = maximumIndex();

    currentIndex = Math.min(
      Math.max(currentIndex, 0),
      maxIndex
    );

    track.style.transform =
      `translateX(-${currentIndex * slideStep()}px)`;

    const visibleCount = visibleSlides();

    slides.forEach((slide, index) => {
      const isVisible =
        index >= currentIndex &&
        index < currentIndex + visibleCount;

      slide.setAttribute(
        'aria-hidden',
        String(!isVisible)
      );
    });

    const dots = Array.from(
      dotsContainer?.children || []
    );

    dots.forEach((dot, index) => {
      const isCurrent = index === currentIndex;

      dot.classList.toggle('is-active', isCurrent);
      dot.setAttribute(
        'aria-current',
        isCurrent ? 'true' : 'false'
      );
    });

    const hasMovement = slides.length > visibleCount;

    previousButton.disabled = !hasMovement;
    nextButton.disabled = !hasMovement;

    if (status) {
      const first = currentIndex + 1;
      const last = Math.min(
        currentIndex + visibleCount,
        slides.length
      );

      status.setAttribute(
        'aria-live',
        announce ? 'polite' : 'off'
      );

      status.textContent =
        `Showing images ${first} to ${last} of ${slides.length}`;
    }
  }

  function move(direction) {
    const maxIndex = maximumIndex();

    if (maxIndex === 0) {
      return;
    }

    if (direction > 0) {
      currentIndex =
        currentIndex >= maxIndex
          ? 0
          : currentIndex + 1;
    } else {
      currentIndex =
        currentIndex <= 0
          ? maxIndex
          : currentIndex - 1;
    }

    updateCarousel(true);
    restartAutoplay();
  }

  function stopAutoplay() {
    window.clearInterval(autoplayTimer);
    autoplayTimer = null;
  }

  function startAutoplay() {
    stopAutoplay();

    if (
      !autoplayEnabled ||
      reducedMotion.matches ||
      maximumIndex() === 0
    ) {
      return;
    }

    autoplayTimer = window.setInterval(() => {
      const maxIndex = maximumIndex();

      currentIndex =
        currentIndex >= maxIndex
          ? 0
          : currentIndex + 1;

      updateCarousel();
    }, autoplayInterval);
  }

  function restartAutoplay() {
    startAutoplay();
  }

  function updateAutoplayButton() {
    if (!autoplayButton) {
      return;
    }

    autoplayButton.textContent = autoplayEnabled
      ? 'Pause autoplay'
      : 'Start autoplay';

    autoplayButton.setAttribute(
      'aria-pressed',
      String(autoplayEnabled)
    );
  }

  previousButton.addEventListener('click', () => {
    move(-1);
  });

  nextButton.addEventListener('click', () => {
    move(1);
  });

  carousel.addEventListener('keydown', event => {
    if (event.key === 'ArrowLeft') {
      event.preventDefault();
      move(-1);
    }

    if (event.key === 'ArrowRight') {
      event.preventDefault();
      move(1);
    }
  });

  carousel.addEventListener(
    'mouseenter',
    stopAutoplay
  );

  carousel.addEventListener(
    'mouseleave',
    startAutoplay
  );

  carousel.addEventListener(
    'focusin',
    stopAutoplay
  );

  carousel.addEventListener('focusout', () => {
    window.setTimeout(() => {
      if (!carousel.contains(document.activeElement)) {
        startAutoplay();
      }
    }, 0);
  });

  carousel.addEventListener('pointerdown', event => {
    if (event.target.closest('button')) {
      return;
    }

    pointerStartX = event.clientX;
  });

  carousel.addEventListener('pointerup', event => {
    if (pointerStartX === null) {
      return;
    }

    const distance = event.clientX - pointerStartX;
    pointerStartX = null;

    if (Math.abs(distance) < 50) {
      return;
    }

    move(distance < 0 ? 1 : -1);
  });

  carousel.addEventListener('pointercancel', () => {
    pointerStartX = null;
  });

  autoplayButton?.addEventListener('click', () => {
    autoplayEnabled = !autoplayEnabled;

    updateAutoplayButton();
    startAutoplay();
  });

  window.addEventListener('resize', () => {
    window.clearTimeout(resizeTimer);

    resizeTimer = window.setTimeout(() => {
      buildDots();
      updateCarousel();
      startAutoplay();
    }, 150);
  });

  reducedMotion.addEventListener?.(
    'change',
    startAutoplay
  );

  buildDots();
  updateAutoplayButton();
  updateCarousel();
  startAutoplay();
}

How the JavaScript Carousel Works

The script finds every element with the .three-image-carousel class:

document
  .querySelectorAll('.three-image-carousel')
  .forEach(initCarousel);

Each carousel is initialized separately. This means the same code can manage multiple sliders on one WordPress page without requiring unique IDs.

Detecting the number of visible images

The visibleSlides() function matches the CSS breakpoints:

function visibleSlides() {
  if (window.innerWidth >= 1024) {
    return 3;
  }

  if (window.innerWidth >= 768) {
    return 2;
  }

  return 1;
}

Keep these values synchronized with your CSS if you change the responsive breakpoints.

Calculating the movement distance

The script measures the width of one slide and adds the gap between slides:

function slideStep() {
  const slideWidth = slides[0].getBoundingClientRect().width;
  const gap =
    Number.parseFloat(getComputedStyle(track).columnGap) || 0;

  return slideWidth + gap;
}

The track is then moved horizontally:

track.style.transform =
  `translateX(-${currentIndex * slideStep()}px)`;

Using the measured pixel width prevents gaps, incorrect positioning, and blank spaces at the end of the carousel.

Preventing the slider from moving too far

The maximum starting position depends on the total number of slides and the number currently visible:

function maximumIndex() {
  return Math.max(0, slides.length - visibleSlides());
}

For example, if the slider contains six images and displays three at a time, the last valid starting position is image four. Images four, five, and six will then appear together.

Responsive navigation dots

The dots are rebuilt when the browser size changes. This is necessary because a desktop layout and a mobile layout have different numbers of valid carousel positions.

Autoplay behavior

Autoplay:

  • Stops when visitors hover over the carousel
  • Stops when keyboard focus enters the carousel
  • Restarts when the interaction ends
  • Can be paused manually
  • Does not start automatically when reduced motion is preferred
  • Uses the interval defined in the HTML

How to Test the Three-Image Slider in CodePen

Follow these steps:

  1. Open a new Pen.
  2. Paste the HTML into the HTML panel.
  3. Paste the CSS into the CSS panel.
  4. Paste the JavaScript into the JavaScript panel.
  5. Check the result preview.
  6. Resize the preview to test the desktop, tablet, and mobile layouts.
  7. Replace the sample image URLs with your own images.
  8. Save the Pen.

No JavaScript library needs to be added through the Pen settings because the carousel uses vanilla JavaScript.

CodePen normally provides separate HTML, CSS, and JavaScript editing panels. It also provides an Embed Builder that allows you to select visible tabs, adjust the embed height, choose a theme, and enable click-to-load behavior.

How to Embed the CodePen Demo in WordPress

Embedding the Pen is useful when you want visitors to see or experiment with the code directly.

After saving the Pen:

  1. Select the Embed option in CodePen.
  2. Configure the displayed tabs and preview.
  3. Copy the generated embed code.
  4. Open your WordPress post or page.
  5. Insert a Custom HTML block.
  6. Paste the embed code.
  7. Preview and publish the page.

CodePen also supports pasting a Pen URL into WordPress or using its provided HTML embed code. The full embed code offers more control over settings such as height, tabs, theme, and click-to-load behavior.

A CodePen embed is suitable for a tutorial or live demonstration. For a production website carousel, adding the code directly to WordPress usually gives you more control over performance, styling, image URLs, and maintenance.

How to Add the Slider Directly to WordPress

There are several ways to install the slider on a WordPress website.

Method 1: Custom HTML Block and a Code-Snippet Tool

This is the most practical method for users who do not want to edit theme files.

Add the HTML

  1. Open the WordPress post or page.
  2. Insert a Custom HTML block.
  3. Paste the slider HTML.
  4. Replace the sample image URLs with images from your WordPress Media Library.
  5. Update the alt text and captions.

WordPress provides the Custom HTML block for adding custom markup. Depending on the WordPress version, hosting configuration, and user permissions, disallowed tags such as scripts may be sanitized when the post is saved. For that reason, it is safer to keep the HTML in the block and load the CSS and JavaScript separately.

Add the CSS

Paste the carousel CSS into one of the following locations:

Add the JavaScript

Add the JavaScript through:

Set the script to load in the footer whenever that option is available.

Method 2: Add the Slider Through a Child Theme

This method is suitable when you control the website theme and want a maintainable production implementation.

Create these files in your child theme:

your-child-theme/
├── assets/
│   ├── css/
│   │   └── three-image-slider.css
│   └── js/
│       └── three-image-slider.js
└── functions.php

Place the CSS in:

/assets/css/three-image-slider.css

Place the JavaScript in:

/assets/js/three-image-slider.js

Add the following PHP code to the child theme’s functions.php file:

<?php

function codecanel_three_image_slider_assets() {
    wp_enqueue_style(
        'codecanel-three-image-slider',
        get_stylesheet_directory_uri() .
            '/assets/css/three-image-slider.css',
        array(),
        '1.0.0'
    );

    wp_enqueue_script(
        'codecanel-three-image-slider',
        get_stylesheet_directory_uri() .
            '/assets/js/three-image-slider.js',
        array(),
        '1.0.0',
        true
    );
}

add_action(
    'wp_enqueue_scripts',
    'codecanel_three_image_slider_assets'
);

WordPress recommends using the wp_enqueue_scripts hook for front-end styles and scripts rather than hardcoding asset tags into theme templates.

You can then place the slider HTML in a Custom HTML block, template file, reusable pattern, or custom block.

Load the files only on a specific page

To avoid loading the carousel assets on every page, add a conditional check:

<?php

function codecanel_three_image_slider_assets() {
    if ( ! is_page( 'slider-demo' ) ) {
        return;
    }

    wp_enqueue_style(
        'codecanel-three-image-slider',
        get_stylesheet_directory_uri() .
            '/assets/css/three-image-slider.css',
        array(),
        '1.0.0'
    );

    wp_enqueue_script(
        'codecanel-three-image-slider',
        get_stylesheet_directory_uri() .
            '/assets/js/three-image-slider.js',
        array(),
        '1.0.0',
        true
    );
}

add_action(
    'wp_enqueue_scripts',
    'codecanel_three_image_slider_assets'
);

Replace slider-demo with the slug of the page containing your carousel.

How to Use WordPress Media Library Images

The sample CodePen uses external placeholder images. Replace them before publishing the slider on a production site.

To find a WordPress image URL:

  1. Open Media from the WordPress dashboard.
  2. Select the image.
  3. Copy its file URL.
  4. Replace the sample src value.
  5. Add accurate alternative text.
  6. Keep the width and height values consistent with the image ratio.

Example:

<img
  src="https://example.com/wp-content/uploads/portfolio-project.jpg"
  alt="Responsive website design displayed on a laptop"
  width="900"
  height="600"
  loading="lazy"
  decoding="async"
>

For the first images visible when the page loads, remove loading="lazy". Images initially outside the visible area can use lazy loading.

How to Add More Images to the Slider

Duplicate one of the <li> elements inside .carousel__track:

<li class="carousel__slide">
  <figure>
    <img
      src="your-image-url.jpg"
      alt="Describe the image"
      width="900"
      height="600"
      loading="lazy"
      decoding="async"
    >
    <figcaption>Your Image Caption</figcaption>
  </figure>
</li>

You do not need to change the JavaScript. It automatically detects the total number of slides and creates the correct navigation dots.

How many images should the carousel contain?

A carousel displaying three items at a time should normally contain at least four images. Six to nine images usually provide a more useful browsing experience without making the carousel excessively long.

How to Remove the Image Captions

Remove each <figcaption> element:

<figcaption>Mountain Landscape</figcaption>

The slider will continue to function normally.

You can also keep the <figure> wrapper without the caption or replace it with a simpler structure.

How to Change the Slider Height

The example uses this image ratio:

.carousel__slide img {
  aspect-ratio: 3 / 2;
}

For square images, use:

aspect-ratio: 1 / 1;

For wider images, use:

aspect-ratio: 16 / 9;

For portrait images, use:

aspect-ratio: 4 / 5;

Because the slider applies object-fit: cover, portions of an image may be cropped to fill the selected ratio.

Use contain instead when the complete image must remain visible:

.carousel__slide img {
  object-fit: contain;
}

This may create blank space around images with different proportions.

How to Change the Number of Visible Slides

The current layout displays one, two, or three images depending on screen width.

Show four images on desktop

Replace the desktop CSS with:

@media (min-width: 1024px) {
  .carousel__slide {
    flex-basis:
      calc(
        (
          100%
          - var(--carousel-gap)
          - var(--carousel-gap)
          - var(--carousel-gap)
        ) / 4
      );
  }
}

Then update the JavaScript:

function visibleSlides() {
  if (window.innerWidth >= 1024) {
    return 4;
  }

  if (window.innerWidth >= 768) {
    return 2;
  }

  return 1;
}

Always show three images

Remove the responsive slide-width rules and use:

.carousel__slide {
  flex:
    0 0
    calc(
      (100% - var(--carousel-gap) - var(--carousel-gap)) / 3
    );
}

However, permanently displaying three images can make the content too narrow on mobile. A responsive one-image mobile layout is generally easier to view and interact with.

How to Change the Transition Speed

The movement speed is controlled by:

.carousel__track {
  transition: transform 0.45s ease;
}

For a faster transition:

transition: transform 0.25s ease;

For a slower transition:

transition: transform 0.8s ease;

Avoid extremely slow transitions because they can make the interface feel unresponsive.

How to Turn Off Autoplay

Change the carousel attribute from:

data-autoplay="true"

to:

data-autoplay="false"

The pause button will become a start button, allowing visitors to activate autoplay manually.

You can completely remove the autoplay button from the HTML if autoplay is never required:

<button
  class="carousel__toggle"
  type="button"
  aria-pressed="true"
>
  Pause autoplay
</button>

The JavaScript uses optional selection for this element, so removing it will not break the carousel.

How to Change the Autoplay Speed

Change the data-interval value:

data-interval="6000"

This changes the delay to six seconds.

The JavaScript enforces a minimum interval of two seconds to avoid extremely rapid automatic movement.

How to Add Multiple Sliders to One WordPress Page

Copy the complete carousel HTML and paste another instance elsewhere on the page.

Example:

<section
  class="three-image-carousel"
  aria-label="Featured products"
  data-autoplay="true"
  data-interval="5000"
>
  <!-- First slider content -->
</section>

<section
  class="three-image-carousel"
  aria-label="Recent portfolio projects"
  data-autoplay="false"
  data-interval="5000"
>
  <!-- Second slider content -->
</section>

The JavaScript initializes every .three-image-carousel independently:

document
  .querySelectorAll('.three-image-carousel')
  .forEach(initCarousel);

Each carousel can therefore have:

  • Different images
  • Different captions
  • A different accessible name
  • A different autoplay setting
  • A different autoplay interval

Do not replace the reusable class with a unique ID unless your project specifically requires one.

Accessibility Best Practices for a Three-Image Carousel

A carousel should not depend only on automatic movement or pointer interaction.

The example includes:

  • A labeled carousel section
  • Proper button elements
  • Descriptive button labels
  • Visible focus indicators
  • Keyboard navigation
  • A pause control
  • Pause on hover
  • Pause when keyboard focus enters the component
  • Slide position labels
  • A status message for manual navigation
  • Reduced-motion support
  • Alternative text for each image

W3C carousel guidance recommends placing a carousel inside a labeled region and representing its collection of items with semantic list markup where appropriate. It also emphasizes usable controls and the ability to stop movement.

Write useful image alt text

Alternative text should describe the image’s relevant content or purpose.

Weak alt text:

alt="Image 1"

More useful alt text:

alt="Modern living room after an interior renovation"

Do not insert the same SEO keyword into every image description. Repetitive or unrelated alt text is not helpful to visitors.

Do not place essential information only inside images

When a slide includes a price, product name, offer, or important message, add that information as real HTML text. Do not rely on text embedded inside the image.

Keep autoplay optional

Automatic movement can make content difficult to follow. Give visitors a clear way to pause it and respect reduced-motion preferences.

WordPress Slider Image SEO and Performance

An image carousel does not automatically improve search rankings. Its SEO value depends on the content, image relevance, loading performance, accessibility, and surrounding page information.

Use descriptive image filenames

Instead of:

IMG_0048.jpg

Use:

responsive-wordpress-carousel-example.jpg

The filename should describe the actual image rather than repeat the same target keyword across every file.

Add contextual alt text

Write alt text based on what each image communicates.

For example:

alt="Three responsive product cards displayed in a WordPress carousel"

Google recommends placing images near relevant text, using short descriptive filenames, and writing useful contextual alt text without keyword stuffing.

Add width and height attributes

The tutorial includes:

width="900"
height="600"

These dimensions allow the browser to reserve the correct space before the image finishes loading, reducing unexpected layout movement.

Use appropriately sized images

Do not upload a 4,000-pixel photograph when the image will be displayed at only a few hundred pixels wide.

Create suitable WordPress image sizes or use responsive image markup so the browser can select an appropriate source.

Use modern image formats

WebP and AVIF can often provide smaller file sizes than traditional formats while maintaining useful visual quality.

Keep a suitable fallback when required by your project.

Use responsive image attributes

WordPress commonly generates srcset and sizes attributes for images selected through its Media Library. These attributes help browsers choose a suitable image size.

A manual example looks like this:

<img
  src="portfolio-900.jpg"
  srcset="
    portfolio-450.jpg 450w,
    portfolio-900.jpg 900w,
    portfolio-1400.jpg 1400w
  "
  sizes="
    (min-width: 1024px) 33vw,
    (min-width: 768px) 50vw,
    100vw
  "
  alt="Web design project displayed in a responsive carousel"
  width="900"
  height="600"
>

Google supports responsive images through srcset and <picture> while recommending a normal src fallback.

Lazy-load only offscreen images

The first images displayed in the carousel may appear within the initial viewport. Avoid delaying an important above-the-fold image with lazy loading.

Use:

loading="lazy"

for later or offscreen slides.

Providing width and height attributes also helps the browser reserve image space and reduce layout shifts.

Avoid excessive JavaScript dependencies

This tutorial does not require jQuery or a third-party carousel library. That reduces the number of dependencies required for this particular slider.

A custom solution is not automatically faster than every plugin. Performance still depends on image sizes, theme code, caching, hosting, and how the files are loaded.

Common WordPress Slider Problems and Solutions

WordPress sliders can sometimes experience layout, responsiveness, JavaScript, image-sizing, or theme-conflict issues. The following solutions cover the most common problems and help you quickly identify why a slider is not displaying or working correctly.

The slider shows only one image on desktop

Check that the desktop media query is present:

@media (min-width: 1024px) {
  .carousel__slide {
    flex-basis:
      calc(
        (100% - var(--carousel-gap) - var(--carousel-gap)) / 3
      );
  }
}

Also confirm that another theme style is not overriding .carousel__slide.

Use your browser’s developer tools to inspect the active flex-basis value.

The slider does not move

Confirm that:

  • The JavaScript file is loading
  • The browser console shows no syntax errors
  • The class names match the tutorial
  • The script runs after the carousel HTML has been added
  • The page contains more slides than the number currently visible

If there are exactly three slides and the desktop layout shows three images, there is no additional content to reveal.

The JavaScript disappears after saving the WordPress page

WordPress may sanitize scripts depending on permissions and configuration. Keep the HTML inside the Custom HTML block and load the JavaScript through a properly enqueued file, code-snippet tool, site-specific plugin, or custom block.

Blank space appears after selecting Next

Blank space commonly appears when a script allows the track to move beyond its final valid position.

This tutorial prevents that with:

function maximumIndex() {
  return Math.max(0, slides.length - visibleSlides());
}

Make sure this logic has not been removed or changed.

Images have different heights

Use a consistent aspect ratio and object-fit:

.carousel__slide img {
  aspect-ratio: 3 / 2;
  object-fit: cover;
}

For the best result, upload images with matching dimensions or proportions.

The theme changes the button styles

A WordPress theme may apply global styles to every <button> element.

The tutorial uses specific classes such as:

.carousel__button
.carousel__toggle
.carousel__dot

Increase selector specificity when necessary:

.three-image-carousel .carousel__button {
  background: rgba(0, 0, 0, 0.72);
}

Avoid styling every button globally.

The carousel overflows on mobile

Check that these rules are present:

.three-image-carousel,
.three-image-carousel * {
  box-sizing: border-box;
}

.carousel__viewport {
  width: 100%;
  overflow: hidden;
}

.carousel__slide {
  min-width: 0;
}

Also inspect the surrounding WordPress column, group, or page-builder container for fixed widths.

Swipe navigation does not work

Confirm that the pointer-event code has been included and that another script is not intercepting pointer gestures.

The viewport should include:

touch-action: pan-y;

This allows normal vertical page scrolling while supporting horizontal swipe detection.

Autoplay continues while visitors are reading a slide

Make sure the hover and focus events are included:

carousel.addEventListener(
  'mouseenter',
  stopAutoplay
);

carousel.addEventListener(
  'focusin',
  stopAutoplay
);

You can also disable autoplay entirely by changing data-autoplay to false.

Navigation dots do not match the number of slides

The dots are calculated from the maximum valid starting position, not simply from the total number of images.

Resize the browser and confirm that buildDots() is called inside the resize handler.

The CodePen demo works, but WordPress does not

This usually indicates one of the following:

  • The theme is overriding the CSS
  • The JavaScript was not loaded
  • WordPress removed the script
  • The image URLs are incorrect
  • A caching or optimization tool is delaying the script incorrectly
  • The CSS or JavaScript was pasted into the wrong location
  • The page builder isolates custom code

Check the browser console and network panel for errors before changing the slider logic.

Custom Code vs a WordPress Slider Plugin

RequirementCustom JavaScript sliderWordPress plugin
Coding requiredYesUsually no
Design controlHighDepends on plugin
Setup speedSlower initiallyUsually faster
External libraryNot required hereDepends on plugin
Visual editorNoOften included
Automatic updatesManual maintenanceUsually available
Multiple slider typesMust be developedMay be included
Best forCustom lightweight layoutsNontechnical management

Use custom code when you need a specific layout and are comfortable maintaining HTML, CSS, and JavaScript.

Use a plugin when editors need to create and update sliders through the WordPress dashboard without modifying code.

Do You Need a Carousel or a Three-Image Comparison Slider?

A carousel presents separate images or cards one after another. A comparison slider is designed to reveal differences between multiple versions of the same subject.

GoalRecommended solution
Browse several products or photographsThree-item carousel in this tutorial
Show recent posts or team membersThree-item carousel
Compare before, during, and after imagesThree-image comparison slider
Reveal visual changes by dragging a handleComparison-slider plugin
Manage sliders without codeWordPress plugin

If your actual goal is to compare three stages of the same image, rather than display three independent cards, Code Canel’s WP Before After Image Slider provides a three-image comparison feature, three labels, captions, shortcode generation, and carousel-related options.

Final Thoughts

Creating a WP slider with three images using HTML, CSS, and JavaScript gives you direct control over the layout, transitions, navigation, responsive behavior, and accessibility.

The carousel in this tutorial displays three images on desktop, two on tablets, and one on mobile. It also includes autoplay controls, navigation dots, swipe gestures, keyboard support, reduced-motion handling, and support for multiple sliders on the same page.

Test the code in CodePen first, replace the sample images with your own WordPress Media Library URLs, and then add the HTML, CSS, and JavaScript to your website using a reliable installation method.

Most importantly, optimize the actual content inside the carousel. Use useful images, descriptive captions, accurate alternative text, properly sized files, and clear navigation. A technically functional slider is valuable only when it helps visitors discover and understand your content.

This page was last edited on 10 July 2026, at 6:48 pm