An image slider can help you display a portfolio, product collection, featured content, testimonials, or promotional banners without using a large amount of page space.

Although WordPress has several slider plugins, you do not always need one. You can create a responsive image slider in WordPress without a plugin using a Custom HTML block, CSS, and a small amount of JavaScript.

In this tutorial, you will build a lightweight WordPress image slider with:

The code is scoped to a unique class, so it is less likely to conflict with your WordPress theme or other elements on the page.

What Is an Image Slider in WordPress?

An image slider, sometimes called an image slideshow or carousel, displays multiple images within one section of a webpage. Visitors can move between the images using arrows, navigation dots, keyboard controls, or swipe gestures.

A slider may also rotate through its images automatically.

Common uses of a WordPress image slider include:

  • Photography and design portfolios
  • Product showcases
  • Featured blog posts
  • Homepage banners
  • Travel galleries
  • Customer testimonials
  • Event highlights
  • Real estate listings
  • Before-and-after project collections

A slider can make a page visually engaging, but it should not contain information that visitors must see to understand the page. Some users may never move beyond the first slide, so your most important content should remain outside the slider or appear on the first slide.

Subscribe to our Newsletter

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

Why Create an Image Slider Without a Plugin?

A plugin is usually the easiest option for advanced sliders. However, creating a simple image slider manually can provide several advantages.

Less Unnecessary Code

Some slider plugins include dozens of templates, animations, integrations, and controls. If you only need a basic image slideshow, much of that code may be unnecessary.

A custom slider lets you load only the HTML, CSS, and JavaScript required for your design.

Greater Design Control

You can control every part of a custom slider, including:

  • Image height
  • Transition speed
  • Button placement
  • Caption styling
  • Autoplay behavior
  • Navigation dots
  • Mobile layout
  • Accessibility features

You are not limited to the settings provided by a plugin.

Fewer Plugin Dependencies

A manually created slider does not depend on a third-party plugin remaining active, compatible, and maintained.

You will still need to review your custom code when making major changes to your WordPress theme, but you retain complete control over it.

Better Coding Experience

Building the slider is also a practical way to understand how HTML, CSS, and JavaScript work together inside WordPress.

When Should You Use a Slider Plugin Instead?

Creating a slider without a plugin is ideal for a small, static collection of images. A plugin may be the better option when you need:

Choose the simplest solution that meets the actual requirements of your website.

Before You Create the Slider

Complete these preparations before adding the code.

Back Up Your Website

Create a recent backup before editing site-wide CSS, JavaScript, or theme files.

For an established website, test the slider on a staging site before adding it to a live page.

Prepare Three or More Images

For the most consistent appearance, use images with the same aspect ratio.

A practical starting size is:

  • Width: 1200 pixels
  • Height: 675 pixels
  • Aspect ratio: 16:9

You can use a different ratio, but all slider images should ideally use the same dimensions.

Optimize the Image Files

Large slider images can slow down the page, especially on mobile devices.

Before uploading them:

  • Resize images to an appropriate display size.
  • Compress each image.
  • Use WebP or AVIF when suitable.
  • Use JPEG for photographs when a newer format is unavailable.
  • Use PNG only when transparency is necessary.
  • Give every file a descriptive name.

For example, use:

wordpress-image-slider-example.webp

Instead of:

IMG_4829.webp

Write Appropriate Alternative Text

Alternative text should explain the content or purpose of an image to someone who cannot see it.

Use specific descriptions such as:

A responsive photography portfolio displayed in a WordPress image slider

Avoid keyword stuffing or descriptions such as:

Image slider WordPress slider best WordPress slider image

If an image is purely decorative and its meaning is already provided in nearby text, an empty alt="" value may be more appropriate.

Step 1: Upload the Slider Images to WordPress

Log in to your WordPress dashboard and follow these steps:

  1. Go to Media → Add New Media File.
  2. Upload the images you want to display.
  3. Open the first uploaded image.
  4. Add accurate alternative text.
  5. Copy the image’s file URL.
  6. Repeat the process for the other images.

Keep the image URLs somewhere accessible. You will replace the sample URLs in the HTML code with these addresses.

Step 2: Add a Custom HTML Block

Open the page or post where you want the image slider to appear.

  1. Click the block inserter.
  2. Search for Custom HTML.
  3. Add the Custom HTML block.
  4. Open its code-editing interface.

You can also type /html in an empty paragraph and select the Custom HTML block.

In newer WordPress versions, the Custom HTML block may provide separate areas for HTML, CSS, and JavaScript. WordPress documentation states that separate editing panels are available beginning with WordPress 7.0. Availability may also depend on the user’s permissions and the site configuration.

Step 3: Add the Image Slider HTML

Paste the following code into the HTML area of the Custom HTML block:

<div
  class="cc-image-slider"
  role="region"
  aria-roledescription="carousel"
  aria-label="Featured image gallery"
  data-autoplay="true"
  data-interval="5000"
>
  <div class="cc-slider__viewport">
    <div class="cc-slider__track">
      <figure
        class="cc-slider__slide"
        aria-roledescription="slide"
      >
        <img
          src="URL_OF_YOUR_IMAGE_1"
          alt="Describe the first image"
          width="1200"
          height="675"
          decoding="async"
          fetchpriority="high"
        >
        <figcaption class="cc-slider__caption">
          Add a short caption for the first image.
        </figcaption>
      </figure>

      <figure
        class="cc-slider__slide"
        aria-roledescription="slide"
      >
        <img
          src="URL_OF_YOUR_IMAGE_2"
          alt="Describe the second image"
          width="1200"
          height="675"
          loading="lazy"
          decoding="async"
        >
        <figcaption class="cc-slider__caption">
          Add a short caption for the second image.
        </figcaption>
      </figure>

      <figure
        class="cc-slider__slide"
        aria-roledescription="slide"
      >
        <img
          src="URL_OF_YOUR_IMAGE_3"
          alt="Describe the third image"
          width="1200"
          height="675"
          loading="lazy"
          decoding="async"
        >
        <figcaption class="cc-slider__caption">
          Add a short caption for the third image.
        </figcaption>
      </figure>
    </div>

    <button
      class="cc-slider__button cc-slider__button--prev"
      type="button"
      aria-label="Show previous slide"
    >
      <span aria-hidden="true">‹</span>
    </button>

    <button
      class="cc-slider__button cc-slider__button--next"
      type="button"
      aria-label="Show next slide"
    >
      <span aria-hidden="true">›</span>
    </button>
  </div>

  <div class="cc-slider__footer">
    <div
      class="cc-slider__dots"
      role="group"
      aria-label="Choose a slide"
    ></div>

    <button class="cc-slider__pause" type="button">
      Pause
    </button>
  </div>

  <p
    class="cc-slider__status cc-visually-hidden"
    aria-live="polite"
    aria-atomic="true"
  ></p>
</div>

Replace these placeholders with your actual WordPress image URLs:

  • URL_OF_YOUR_IMAGE_1
  • URL_OF_YOUR_IMAGE_2
  • URL_OF_YOUR_IMAGE_3

Also replace the alternative text and captions.

Why the First Image Is Not Lazy-Loaded

The first slide is immediately visible, so it should normally begin loading without waiting for a lazy-loading threshold.

The other images use native lazy loading to reduce unnecessary initial requests. Their real image addresses remain in the src attributes, which helps browsers and search engines discover them without depending on a click or swipe.

Google recommends that relevant lazy-loaded content become available when it enters the viewport rather than depending on user actions such as clicking.

Step 4: Add the Slider CSS

Paste the following CSS into the CSS panel of the Custom HTML editor when that option is available:

.cc-image-slider,
.cc-image-slider *,
.cc-image-slider *::before,
.cc-image-slider *::after {
  box-sizing: border-box;
}

.cc-image-slider {
  --cc-slider-radius: 16px;
  --cc-slider-control-size: 44px;
  --cc-slider-caption-bg: rgba(0, 0, 0, 0.68);

  position: relative;
  width: 100%;
  max-width: 1200px;
  margin: 2rem auto;
}

.cc-slider__viewport {
  position: relative;
  overflow: hidden;
  border-radius: var(--cc-slider-radius);
  background: #111;
}

.cc-slider__track {
  display: flex;
  width: 100%;
  transition: transform 450ms ease;
  will-change: transform;
}

.cc-slider__slide {
  position: relative;
  flex: 0 0 100%;
  width: 100%;
  margin: 0;
  overflow: hidden;
  aspect-ratio: 16 / 9;
}

.cc-slider__slide img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.cc-slider__caption {
  position: absolute;
  right: 0;
  bottom: 0;
  left: 0;
  padding: 1rem 1.25rem;
  color: #fff;
  font-size: clamp(0.9rem, 2vw, 1.05rem);
  line-height: 1.5;
  background: var(--cc-slider-caption-bg);
}

.cc-slider__button {
  position: absolute;
  top: 50%;
  display: grid;
  place-items: center;
  width: var(--cc-slider-control-size);
  height: var(--cc-slider-control-size);
  padding: 0;
  border: 0;
  border-radius: 50%;
  color: #111;
  background: rgba(255, 255, 255, 0.9);
  box-shadow: 0 4px 16px rgba(0, 0, 0, 0.24);
  font-size: 2rem;
  line-height: 1;
  cursor: pointer;
  transform: translateY(-50%);
}

.cc-slider__button:hover {
  background: #fff;
}

.cc-slider__button--prev {
  left: 1rem;
}

.cc-slider__button--next {
  right: 1rem;
}

.cc-slider__footer {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  padding-top: 0.85rem;
}

.cc-slider__dots {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.55rem;
}

.cc-slider__dot {
  width: 12px;
  height: 12px;
  padding: 0;
  border: 2px solid currentColor;
  border-radius: 50%;
  color: #333;
  background: transparent;
  cursor: pointer;
}

.cc-slider__dot.is-active {
  background: currentColor;
}

.cc-slider__pause {
  min-height: 40px;
  padding: 0.5rem 0.9rem;
  border: 1px solid currentColor;
  border-radius: 6px;
  color: inherit;
  background: transparent;
  font: inherit;
  cursor: pointer;
}

.cc-slider__button:focus-visible,
.cc-slider__dot:focus-visible,
.cc-slider__pause:focus-visible {
  outline: 3px solid currentColor;
  outline-offset: 3px;
}

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

@media (max-width: 600px) {
  .cc-image-slider {
    --cc-slider-control-size: 38px;
    --cc-slider-radius: 10px;
  }

  .cc-slider__button--prev {
    left: 0.6rem;
  }

  .cc-slider__button--next {
    right: 0.6rem;
  }

  .cc-slider__caption {
    padding: 0.75rem 1rem;
  }

  .cc-slider__footer {
    align-items: flex-start;
  }
}

@media (prefers-reduced-motion: reduce) {
  .cc-slider__track {
    transition: none;
  }
}

Where to Add the CSS in a Classic Theme

When your WordPress theme uses the Customizer:

  1. Go to Appearance → Customize.
  2. Select Additional CSS.
  3. Paste the slider CSS.
  4. Click Publish.

Where to Add the CSS in a Block Theme

When your site uses a block theme:

  1. Go to Appearance → Editor.
  2. Open the Styles panel.
  3. Open the three-dot menu.
  4. Select Additional CSS.
  5. Paste the CSS and save your changes.

WordPress includes a site-wide CSS editor inside the Styles interface for block themes.

Do not paste the complete slider stylesheet into the per-block CSS area. The code contains several custom selectors and should be added to the site-wide Additional CSS field.

Step 5: Add the Slider JavaScript

Paste the following code into the JavaScript panel of the Custom HTML editor:

document.addEventListener('DOMContentLoaded', () => {
  document.querySelectorAll('.cc-image-slider').forEach((slider) => {
    const track = slider.querySelector('.cc-slider__track');
    const slides = Array.from(
      slider.querySelectorAll('.cc-slider__slide')
    );
    const previousButton = slider.querySelector(
      '.cc-slider__button--prev'
    );
    const nextButton = slider.querySelector(
      '.cc-slider__button--next'
    );
    const pauseButton = slider.querySelector(
      '.cc-slider__pause'
    );
    const dotsContainer = slider.querySelector(
      '.cc-slider__dots'
    );
    const status = slider.querySelector(
      '.cc-slider__status'
    );

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

    const autoplayEnabled =
      slider.dataset.autoplay === 'true';

    const interval = Math.max(
      Number(slider.dataset.interval) || 5000,
      3000
    );

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

    let currentIndex = 0;
    let timerId = null;
    let isPaused = reduceMotion || !autoplayEnabled;
    let touchStartX = 0;

    const dots = slides.map((slide, index) => {
      slide.setAttribute(
        'aria-label',
        `${index + 1} of ${slides.length}`
      );

      const dot = document.createElement('button');
      dot.type = 'button';
      dot.className = 'cc-slider__dot';
      dot.setAttribute(
        'aria-label',
        `Show slide ${index + 1}`
      );

      dot.addEventListener('click', () => {
        goToSlide(index, true);
      });

      dotsContainer?.appendChild(dot);
      return dot;
    });

    function setFocusableState(slide, isActive) {
      const focusableElements = slide.querySelectorAll(
        'a, button, input, select, textarea, [tabindex]'
      );

      focusableElements.forEach((element) => {
        if (!isActive) {
          if (
            !element.hasAttribute(
              'data-cc-original-tabindex'
            )
          ) {
            const originalTabindex =
              element.getAttribute('tabindex');

            element.setAttribute(
              'data-cc-original-tabindex',
              originalTabindex === null
                ? ''
                : originalTabindex
            );
          }

          element.setAttribute('tabindex', '-1');
        } else if (
          element.hasAttribute(
            'data-cc-original-tabindex'
          )
        ) {
          const originalTabindex =
            element.getAttribute(
              'data-cc-original-tabindex'
            );

          if (originalTabindex === '') {
            element.removeAttribute('tabindex');
          } else {
            element.setAttribute(
              'tabindex',
              originalTabindex
            );
          }

          element.removeAttribute(
            'data-cc-original-tabindex'
          );
        }
      });
    }

    function updateSlider(announce = false) {
      track.style.transform =
        `translateX(-${currentIndex * 100}%)`;

      slides.forEach((slide, index) => {
        const isActive = index === currentIndex;

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

        setFocusableState(slide, isActive);
      });

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

        dot.classList.toggle(
          'is-active',
          isActive
        );

        dot.setAttribute(
          'aria-current',
          isActive ? 'true' : 'false'
        );
      });

      if (announce && status) {
        status.textContent =
          `Slide ${currentIndex + 1} of ${slides.length}`;
      }
    }

    function stopTimer() {
      window.clearInterval(timerId);
      timerId = null;
    }

    function startTimer() {
      stopTimer();

      if (
        !autoplayEnabled ||
        isPaused ||
        slides.length < 2
      ) {
        return;
      }

      timerId = window.setInterval(() => {
        currentIndex =
          (currentIndex + 1) % slides.length;

        updateSlider(false);
      }, interval);
    }

    function goToSlide(index, announce = true) {
      currentIndex =
        (index + slides.length) % slides.length;

      updateSlider(announce);
      startTimer();
    }

    function updatePauseButton() {
      if (!pauseButton) {
        return;
      }

      pauseButton.hidden =
        !autoplayEnabled || slides.length < 2;

      pauseButton.textContent =
        isPaused ? 'Play' : 'Pause';

      pauseButton.setAttribute(
        'aria-label',
        isPaused
          ? 'Play automatic slideshow'
          : 'Pause automatic slideshow'
      );
    }

    previousButton?.addEventListener(
      'click',
      () => {
        goToSlide(currentIndex - 1, true);
      }
    );

    nextButton?.addEventListener(
      'click',
      () => {
        goToSlide(currentIndex + 1, true);
      }
    );

    pauseButton?.addEventListener(
      'click',
      () => {
        isPaused = !isPaused;
        updatePauseButton();
        startTimer();
      }
    );

    slider.addEventListener(
      'keydown',
      (event) => {
        if (event.key === 'ArrowLeft') {
          event.preventDefault();
          goToSlide(currentIndex - 1, true);
        }

        if (event.key === 'ArrowRight') {
          event.preventDefault();
          goToSlide(currentIndex + 1, true);
        }

        if (event.key === 'Home') {
          event.preventDefault();
          goToSlide(0, true);
        }

        if (event.key === 'End') {
          event.preventDefault();
          goToSlide(slides.length - 1, true);
        }
      }
    );

    slider.addEventListener(
      'mouseenter',
      stopTimer
    );

    slider.addEventListener(
      'mouseleave',
      startTimer
    );

    slider.addEventListener(
      'focusin',
      stopTimer
    );

    slider.addEventListener(
      'focusout',
      (event) => {
        if (!slider.contains(event.relatedTarget)) {
          startTimer();
        }
      }
    );

    slider.addEventListener(
      'touchstart',
      (event) => {
        touchStartX =
          event.changedTouches[0].clientX;

        stopTimer();
      },
      { passive: true }
    );

    slider.addEventListener(
      'touchend',
      (event) => {
        const touchDistance =
          event.changedTouches[0].clientX -
          touchStartX;

        if (Math.abs(touchDistance) > 50) {
          goToSlide(
            currentIndex +
              (touchDistance < 0 ? 1 : -1),
            true
          );
        } else {
          startTimer();
        }
      },
      { passive: true }
    );

    document.addEventListener(
      'visibilitychange',
      () => {
        if (document.hidden) {
          stopTimer();
        } else {
          startTimer();
        }
      }
    );

    if (slides.length < 2) {
      previousButton?.setAttribute(
        'hidden',
        ''
      );

      nextButton?.setAttribute(
        'hidden',
        ''
      );

      dotsContainer?.setAttribute(
        'hidden',
        ''
      );
    }

    updateSlider(false);
    updatePauseButton();
    startTimer();
  });
});

How to Add JavaScript Without Using a Plugin

The best method depends on your WordPress version, theme, hosting configuration, and user permissions.

Method 1: Use the Custom HTML JavaScript Panel

When the Custom HTML block provides separate JavaScript editing, paste the code into that panel.

This is the simplest method because the slider code remains connected to the block containing the slider.

Method 2: Place the Script Under the HTML

On an older WordPress installation, you may be able to place the JavaScript inside <script> tags below the slider HTML:

<script>
  // Paste the JavaScript here.
</script>

However, WordPress may remove scripts when the user does not have the required unfiltered HTML capability. This is especially relevant on multisite installations or for non-administrator accounts.

After saving the page, reopen the block and confirm that the script is still present.

Method 3: Enqueue the Script Through a Child Theme

For a cleaner site-wide implementation, save the JavaScript in a child theme.

Create this file:

/assets/js/codecanel-image-slider.js

Paste the JavaScript into that file.

Then add the following PHP to the child theme’s functions.php file:

<?php
function codecanel_enqueue_image_slider_script() {
	wp_enqueue_script(
		'codecanel-image-slider',
		get_stylesheet_directory_uri() . '/assets/js/codecanel-image-slider.js',
		array(),
		'1.0.0',
		array(
			'strategy'  => 'defer',
			'in_footer' => true,
		)
	);
}

add_action(
	'wp_enqueue_scripts',
	'codecanel_enqueue_image_slider_script'
);

WordPress recommends loading front-end JavaScript with wp_enqueue_script() instead of manually adding script tags throughout a theme.

Use a child theme rather than editing a parent theme directly. Otherwise, a theme update may overwrite your custom file and PHP changes.

Internal link opportunity: Link “child theme” to your existing WordPress child-theme tutorial.

Step 6: Preview and Test the Image Slider

Save the page and open its front-end preview.

Confirm that:

  • The first image appears initially.
  • The previous button displays the last slide when used on the first slide.
  • The next button returns to the first slide after the final slide.
  • Every navigation dot displays the correct image.
  • The play and pause button works.
  • Autoplay pauses when the pointer is over the slider.
  • Autoplay pauses when a control receives keyboard focus.
  • The left and right arrow keys change slides.
  • The Home key opens the first slide.
  • The End key opens the final slide.
  • Swipe gestures work on a touchscreen.
  • Captions remain readable.
  • Images do not stretch or create layout shifts.
  • The slider works on desktop, tablet, and mobile screens.

Also test the page while logged out of WordPress. A slider can work in the editor preview but fail on the public page because of caching, optimization, or script-loading settings.

How the WordPress Image Slider Code Works

Understanding the code will make it easier to customize.

The Slider Container

The .cc-image-slider element contains one complete slider.

Because the JavaScript searches for every element with this class, you can place multiple independent sliders on one page.

The Viewport

The .cc-slider__viewport element hides the slides that are outside the visible area.

Its overflow: hidden rule prevents the page from displaying the entire horizontal row.

The Track

The .cc-slider__track element holds all the slides in a flexible row.

The JavaScript changes its horizontal position with:

transform: translateX(...);

When the active slide changes, the track moves by 100% of the slider width.

The Slides

Each .cc-slider__slide uses:

flex: 0 0 100%;

This makes every slide occupy the full width of the viewport.

Unlike approaches that manually set the track to 300%, this method continues to work when you add or remove slides.

The Navigation Dots

The JavaScript automatically creates one navigation dot for every slide. You do not need to add the dot buttons manually.

The active dot receives the .is-active class and an aria-current attribute.

The Autoplay Timer

The data-interval attribute controls the delay between automatic changes.

The slider stops its timer when:

  • A visitor pauses it
  • The pointer enters the slider
  • Keyboard focus enters the slider
  • The browser tab becomes hidden
  • Reduced-motion preferences are enabled

The Accessibility Status

The visually hidden status element announces manually selected slides to screen-reader users without placing extra text on the visible design.

How to Change the Autoplay Speed

Find this line in the opening slider element:

data-interval="5000"

The value is measured in milliseconds.

Examples:

data-interval="3000"

Changes the slide every three seconds.

data-interval="7000"

Changes the slide every seven seconds.

Avoid extremely fast transitions. Visitors need enough time to understand each image and caption.

How to Disable Autoplay

Change:

data-autoplay="true"

To:

data-autoplay="false"

The play and pause button will be hidden automatically. Visitors can still use the arrows, dots, keyboard commands, and swipe gestures.

Disabling autoplay is often the better choice when slides contain important text.

How to Change the Image Slider Height

The slide currently uses a 16:9 aspect ratio:

aspect-ratio: 16 / 9;

For a wider banner, use:

aspect-ratio: 21 / 9;

For a square slider, use:

aspect-ratio: 1 / 1;

For a portrait slider, use:

aspect-ratio: 4 / 5;

The images use:

object-fit: cover;

This fills the available area but may crop the edges of an image.

To show the entire image without cropping, change it to:

object-fit: contain;

This may create empty space around images that do not match the slider’s aspect ratio.

How to Add More Images to the Slider

Copy one complete slide:

<figure
  class="cc-slider__slide"
  aria-roledescription="slide"
>
  <img
    src="URL_OF_YOUR_NEW_IMAGE"
    alt="Describe the new image"
    width="1200"
    height="675"
    loading="lazy"
    decoding="async"
  >
  <figcaption class="cc-slider__caption">
    Add a caption for the new image.
  </figcaption>
</figure>

Paste it inside .cc-slider__track.

The JavaScript automatically counts the new slide and creates an additional navigation dot.

You do not need to change a width calculation or update the JavaScript.

How to Remove Image Captions

Delete the <figcaption> element from each slide:

<figcaption class="cc-slider__caption">
  Add a short caption.
</figcaption>

Do not remove the <figure> element or its .cc-slider__slide class.

How to Add Text Over Slider Images

The current caption already appears as a text overlay at the bottom of each image.

You can include a heading and description inside the caption:

<figcaption class="cc-slider__caption">
  <strong>Explore Our Latest Project</strong>
  <span>
    See how we redesigned this WordPress website.
  </span>
</figcaption>

Add this CSS to display the elements on separate lines:

.cc-slider__caption strong,
.cc-slider__caption span {
  display: block;
}

.cc-slider__caption strong {
  margin-bottom: 0.25rem;
  font-size: 1.15em;
}

Keep overlay text concise. Large paragraphs are difficult to read before an automatic transition.

How to Link a Slider Image

Wrap the image in an anchor element:

<a href="YOUR_DESTINATION_URL">
  <img
    src="URL_OF_YOUR_IMAGE"
    alt="Describe the destination shown in the image"
    width="1200"
    height="675"
    loading="lazy"
    decoding="async"
  >
</a>

Use a destination that clearly matches the image and caption.

Avoid opening ordinary internal links in a new browser tab unless there is a specific reason.

The provided JavaScript removes links in hidden slides from the keyboard tab sequence and restores them when their slide becomes active.

How to Add Multiple Image Sliders to One WordPress Page

Copy the entire .cc-image-slider HTML component and paste it somewhere else on the page.

Replace the images and captions in the copy.

You only need to add the CSS and JavaScript once. The JavaScript initializes every .cc-image-slider separately, so the controls of one slider should not change another slider.

This is an important advantage over code that uses global selectors such as:

document.querySelector('.next');

A global selector usually finds only the first matching button and can cause conflicts when multiple sliders are present.

How to Add an Image Slider to the WordPress Homepage

The process depends on how your homepage is built.

Static Homepage

Open the page assigned as the homepage and add the Custom HTML block where you want the slider to appear.

Block-Theme Homepage Template

Go to Appearance → Editor, open the homepage or front-page template, and add a Custom HTML block.

Be careful when editing templates because a template-level slider may appear on multiple pages.

Page Builder Homepage

Add an HTML or code widget in the page builder. Place the markup in the widget, then add the CSS and JavaScript through the builder’s supported custom-code locations.

How to Add an Image Slider to a WordPress Post

Open the post and add a Custom HTML block at the desired location.

Place the slider near the section it supports. Avoid putting a large slider before the introduction unless the images are the primary purpose of the article.

How to Add an Image Slider to a WordPress Header

Adding a slider to a header is more advanced because headers are normally controlled by the theme.

With a block theme:

  1. Go to Appearance → Editor.
  2. Open the header template part.
  3. Add the slider block.
  4. Check how the header appears on all templates.

With a classic theme, use a child theme and edit the appropriate header template file.

A header slider may appear across the entire website and can significantly affect page-loading performance. Use it only when the slider is important on every page.

How to Make the Image Slider More Accessible

Accessibility should be included in the initial slider design rather than added after publishing.

W3C guidance for carousels recommends allowing visitors to pause movement, operate all functions with a keyboard, and understand when the active slide changes.

The slider in this tutorial includes:

  • Semantic button elements
  • Descriptive control labels
  • Keyboard navigation
  • A visible pause control
  • Hover and focus pausing
  • Screen-reader status announcements
  • Active-slide labels
  • Visible focus outlines
  • Reduced-motion support
  • Hidden-slide focus management

You should also review the content itself.

Keep Alternative Text Useful

Describe the purpose or content of each image. Do not repeat the caption word for word unless that is genuinely the best description.

Do Not Place Essential Instructions Only in a Slide

A visitor may not discover every slide. Important instructions, prices, deadlines, warnings, and calls to action should also be available outside the carousel.

Keep Controls Easy to See

Arrow buttons and navigation dots need sufficient contrast against the page and image backgrounds.

Let Users Stop Movement

The slider includes a pause button and automatically stops while a visitor hovers over it or moves keyboard focus into it.

W3C also recommends pausing carousel animation while the component is hovered or focused.

Respect Reduced-Motion Preferences

The CSS removes the transition animation when a visitor has requested reduced motion. The JavaScript also prevents autoplay from starting automatically.

The visitor can still select Play manually.

How to Optimize a WordPress Image Slider for Speed and SEO

An image slider is often one of the largest visual elements on a page. Poor image preparation can therefore affect loading speed and visual stability.

Use Correct Image Dimensions

Do not upload a 5000-pixel photograph when the slider displays it at approximately 1200 pixels.

Resize images before uploading while retaining enough resolution for larger screens.

Define Width and Height

Every image in the example contains:

width="1200"
height="675"

These attributes help the browser reserve the correct amount of space before the image finishes loading.

This can reduce sudden layout movement.

Use Responsive WordPress Images When Possible

Images inserted through WordPress’s Image block normally receive responsive srcset and sizes attributes automatically.

Because this tutorial uses manually written image markup, you can add responsive attributes yourself when you have multiple generated image sizes:

<img
  src="image-1200.webp"
  srcset="
    image-480.webp 480w,
    image-768.webp 768w,
    image-1200.webp 1200w
  "
  sizes="(max-width: 1200px) 100vw, 1200px"
  alt="Describe the image"
  width="1200"
  height="675"
>

Only include URLs for files that actually exist.

Prioritize the First Visible Image

When the slider appears near the top of the page, the first image may become an important loading element.

The example uses:

fetchpriority="high"

On the first image and does not lazy-load it.

Do not apply high priority to every slide. Doing so can reduce the value of the priority hint and create unnecessary competition between downloads.

Lazy-Load Later Images Carefully

Native lazy loading can be useful for slides that are not initially visible.

However, do not create a system in which image URLs are added only after a user clicks an arrow. Google explains that it does not perform user actions such as clicking to reveal lazy-loaded content.

Compress Every Image

Use an image-compression workflow before publishing.

Compare the final appearance rather than choosing a compression level based only on file size. Excessive compression may make product details, text, or portfolio images appear unclear.

Limit the Number of Slides

Adding 20 large images can make the slider slow even when later slides are lazy-loaded.

For a homepage or promotional slider, three to five focused slides are usually easier to manage than a large collection.

For a substantial gallery, consider a dedicated gallery layout instead.

Use Descriptive Context

Search engines do not depend only on alternative text. Add a useful heading, introductory paragraph, and relevant caption around the slider so the images have clear context.

Test Core Web Vitals

After publishing, test the page for:

  • Largest Contentful Paint
  • Cumulative Layout Shift
  • Interaction to Next Paint

Google describes Core Web Vitals as measurements of loading performance, interactivity, and visual stability.

Compare the results before and after adding the slider.

Common WordPress Image Slider Problems and Solutions

All Slider Images Are Showing at Once

Possible cause: The CSS did not load or the class names were changed.

Solution:

Confirm that:

  • The CSS is present on the public page.
  • The viewport has overflow: hidden.
  • The track has display: flex.
  • Every slide has flex: 0 0 100%.
  • The HTML class names match the CSS exactly.

Clear your WordPress cache after changing the CSS.

The Slider Shows a Blank Area

Possible cause: An image URL is incorrect.

Open each image URL directly in a browser. If it returns an error or redirects to a login page, replace it with the public Media Library file URL.

Also confirm that the image is not being blocked by a hotlink-protection or security rule.

The Slider Buttons Do Not Work

Possible cause: The JavaScript did not load.

Check whether:

  • The script remains present after saving.
  • The JavaScript was added after the HTML.
  • Your cache contains an older script file.
  • A performance tool delayed or combined the script incorrectly.
  • The browser console reports a JavaScript error.

When using a child theme, confirm that the file path in functions.php matches the actual JavaScript file location.

The Second Slide Appears First

Possible cause: The code increases the slide index before rendering the initial slide.

The JavaScript in this tutorial starts with index zero and renders that index before starting autoplay.

Avoid calling an increment function before the first render.

The Images Have Different Heights

Possible cause: The images use different aspect ratios.

The tutorial solves this with:

aspect-ratio: 16 / 9;
object-fit: cover;

You can also crop all images to the same dimensions before uploading.

The Slider Is Not Responsive on Mobile

Possible cause: A fixed pixel width was applied to the slider or slides.

Keep the container at width: 100% and use percentage-based slide widths.

Do not change the track to a vertical layout on mobile unless you intentionally want a stacked image gallery instead of a slider.

The Slider Is Cutting Off Important Parts of an Image

Possible cause: object-fit: cover is cropping the image.

Change it to:

object-fit: contain;

Alternatively, edit the source image so its important subject fits within the chosen aspect ratio.

Autoplay Does Not Start

Possible causes:

  • data-autoplay is set to false.
  • The visitor prefers reduced motion.
  • The slider has only one image.
  • A JavaScript error stopped initialization.
  • The browser tab is inactive.
  • Keyboard focus or the pointer is currently inside the slider.

Use the Play button to start autoplay when reduced motion initially disables it.

The Autoplay Speed Does Not Change

Possible cause: The interval is below the minimum set in the JavaScript.

This line prevents intervals shorter than three seconds:

Math.max(
  Number(slider.dataset.interval) || 5000,
  3000
);

To permit a shorter interval, change 3000, although fast image changes are generally not recommended.

Two Sliders Control Each Other

Possible cause: The JavaScript uses selectors that are not scoped to each slider.

Use the complete script from this tutorial. It loops through every slider container and searches for controls only inside the current container.

JavaScript Disappears After Saving the Page

Possible cause: WordPress removed disallowed script markup.

Use an administrator account with the necessary permissions or enqueue the JavaScript through a child theme.

Do not attempt to bypass site security restrictions. Contact the site administrator when you do not have permission to add scripts.

The Customizer Is Missing

Possible cause: The site uses a block theme.

Block themes use the Site Editor instead of the classic Customizer. WordPress distinguishes block themes from classic themes and provides block-theme controls through Appearance → Editor.

Use the Styles panel’s site-wide Additional CSS area.

Changes Are Not Appearing

Clear:

Then test the page in a private browser window.

Also confirm that you edited the correct page, template, or theme.

CSS-Only Image Slider: Is It Possible?

Yes, it is possible to build an image slider using CSS without JavaScript. A CSS-only slider may use radio buttons, anchor targets, scroll snapping, or CSS animations.

However, CSS-only sliders have limitations:

  • Autoplay controls can be difficult to implement accessibly.
  • State management becomes less flexible.
  • Navigation logic may require repetitive markup.
  • Keyboard behavior can be less intuitive.
  • Multiple-slider management can become complicated.
  • Screen-reader announcements are harder to control.

For a basic manually controlled gallery, CSS scroll snapping can be an effective alternative. For arrows, dots, autoplay, pause controls, and active-slide announcements, a small JavaScript component is generally more practical.

Image Slider vs Image Gallery: Which Should You Use?

Use an image slider when:

  • You need to conserve vertical space.
  • The images have a clear sequence.
  • The first slide provides enough context.
  • Visitors do not need to compare all images simultaneously.

Use an image gallery when:

  • Every image is important.
  • Visitors should scan several images quickly.
  • The order is not essential.
  • Comparing thumbnails is useful.
  • Discoverability matters more than compactness.

A gallery often provides better visibility because users can see multiple images without operating controls.

Internal link opportunity: Link this section to your carousel-versus-gallery article.

Image Slider vs Before-and-After Slider

A standard image slider displays separate images one at a time.

A before-and-after slider places two related images in the same frame and lets visitors reveal the difference by dragging a divider.

A before-and-after comparison is more suitable for:

  • Photo editing
  • Renovation projects
  • Beauty treatments
  • Restoration work
  • Product improvements
  • Design revisions
  • Fitness or landscaping progress

When the purpose is direct visual comparison, use a dedicated before-and-after component rather than a standard slideshow.

Final Thoughts

Creating an image slider in WordPress without a plugin gives you direct control over its design, behavior, performance, and accessibility.

The process involves three main elements:

  1. HTML creates the slider structure.
  2. CSS controls its layout and appearance.
  3. JavaScript manages navigation, autoplay, keyboard controls, swipe gestures, and active-slide states.

The most important part is not simply making the images move. A useful WordPress image slider should also load efficiently, remain responsive, support keyboard users, allow visitors to pause movement, and avoid conflicts with other components.

Frequently Asked Questions

Can I Create an Image Slider in WordPress Without Coding Experience?

You can follow the supplied code without writing it from scratch, but you should be comfortable copying code, replacing image URLs, and troubleshooting minor errors. Create a backup and test the slider on a staging site when possible.

Can I Add an Image Slider to Any WordPress Theme?

Most properly developed WordPress themes can display the slider because it uses standard HTML, CSS, and JavaScript.

Can I Create a WordPress Image Slider Without JavaScript?

Yes. You can use CSS scroll snapping, radio buttons, or CSS animations. However, JavaScript provides more reliable control over arrows, navigation dots, autoplay, keyboard interaction, and accessibility announcements.

What Is the Best Image Size for a WordPress Slider?

There is no universal size for every theme. A 1200 × 675-pixel image is a practical starting point for a wide content slider. Use the dimensions required by your actual layout and keep all images at the same aspect ratio.

Can I Add Text Over the Slider Images?

Yes. Use the <figcaption> element included in the HTML. The provided CSS displays it over the bottom of the image. Keep the text brief and maintain strong contrast.

Can Every Slider Image Have a Different Link?

Yes. Wrap each image in its own anchor element and use the appropriate destination URL. Make sure the link’s purpose is understandable from the image, caption, or nearby text.

Can I Put Multiple Sliders on the Same Page?

Yes. Duplicate the complete HTML component. The JavaScript initializes each slider independently. Add the CSS and JavaScript only once.

Is a Custom Slider Better Than a Plugin?

A custom slider is suitable when you need a small, static, carefully controlled component. A plugin is better when non-technical users need to manage slides regularly or when the website requires advanced dynamic features.

This page was last edited on 22 July 2026, at 5:58 pm