Type something to search...
How to Add a Before-and-After Image Slider in WordPress?

How to Add a Before-and-After Image Slider in WordPress?

A before-and-after slider lets a visitor drag a single handle to reveal the difference between two images, and it's one of the most persuasive visuals a results-driven site can use. Home renovation, dental and cosmetic work, landscaping, weight loss coaching, photo editing services, and web redesign agencies all rely on this exact format because it makes a transformation immediately obvious in a way two side-by-side photos never quite manage.

You've got two solid routes: a purpose-built plugin that handles the drag interaction and touch support for you, or a hand-rolled slider using nothing but HTML, CSS, and a small amount of JavaScript. This guide covers both, including a complete, working custom implementation using the clip-path technique.

How a Before-and-After Slider Actually Works

Every version of this effect, plugin or custom, relies on the same underlying trick: two images are stacked exactly on top of each other, and the top image is clipped (or resized) based on a handle's horizontal position, revealing the bottom image underneath as the visitor drags. The handle position is what changes; the mechanism clipping the top image is the only real technical decision to make.

Option 1: Use a Plugin

Twenty20 Image Before-After

Twenty20 Image Before-After is a dedicated, actively maintained plugin built specifically for this effect, wrapping the popular open-source twentytwenty jQuery slider in a WordPress-friendly shortcode and block:

  • Upload a "before" and "after" image directly through a Gutenberg block or shortcode interface.
  • Choose between a horizontal or vertical drag orientation.
  • Optional "before"/"after" text overlay labels.
  • Touch and mouse drag support out of the box, with no custom JavaScript required.

Setup:

  1. Install and activate Twenty20 Image Before-After from Plugins > Add New.
  2. In the block editor, add the Twenty20 Image Before-After block, or use the shortcode form: [twenty20 img1="before.jpg" img2="after.jpg" offset="0.5"].
  3. Set offset to control where the handle starts (a value between 0 and 1, where 0.5 centers it).
  4. Preview the page to confirm both images are the same dimensions; mismatched aspect ratios are the most common cause of a slider looking stretched or off.

Envira Gallery's Before/After Add-On

If you're already using Envira Gallery for a broader portfolio gallery, its paid before/after add-on lets you add this same effect without introducing a second plugin, which is worth considering if before/after comparisons are just one section among several image-heavy features on the site.

Either plugin route gets you working drag behavior, proper touch support on mobile, and reasonable defaults in a few minutes, without needing to think about the clip-path mechanics covered next.

Option 2: Build a Custom Before-and-After Slider

If you'd rather not add a plugin for a single effect, or you want the interaction styled to match your site exactly, the version below is a complete, working implementation using clip-path, one of the two standard techniques for this effect (the other being an overlay <img> with an adjustable width, which clips less cleanly at the edges on non-uniform images).

Step 1: HTML Structure

<div class="tw-ba-slider" id="ba-slider-1">
  <div class="tw-ba-image tw-ba-after">
    <img src="/images/kitchen-after.jpg" alt="Kitchen after renovation" />
    <span class="tw-ba-label tw-ba-label--after">After</span>
  </div>
  <div class="tw-ba-image tw-ba-before">
    <img src="/images/kitchen-before.jpg" alt="Kitchen before renovation" />
    <span class="tw-ba-label tw-ba-label--before">Before</span>
  </div>
  <div class="tw-ba-handle">
    <div class="tw-ba-handle-line"></div>
    <div class="tw-ba-handle-circle">&#8596;</div>
  </div>
</div>

The "after" image sits underneath as the full, unclipped base layer; the "before" image sits on top and gets clipped by JavaScript as the visitor drags.

Step 2: CSS

.tw-ba-slider {
  position: relative;
  width: 100%;
  max-width: 800px;
  aspect-ratio: 4 / 3;
  overflow: hidden;
  border-radius: 8px;
  user-select: none;
  cursor: ew-resize;
}

.tw-ba-image {
  position: absolute;
  inset: 0;
}

.tw-ba-image img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

.tw-ba-before {
  clip-path: inset(0 50% 0 0);
}

.tw-ba-label {
  position: absolute;
  bottom: 12px;
  padding: 4px 10px;
  background: rgba(0, 0, 0, 0.6);
  color: #fff;
  font-size: 0.8rem;
  border-radius: 4px;
}

.tw-ba-label--before {
  left: 12px;
}

.tw-ba-label--after {
  right: 12px;
}

.tw-ba-handle {
  position: absolute;
  top: 0;
  bottom: 0;
  left: 50%;
  width: 0;
  transform: translateX(-50%);
  pointer-events: none;
}

.tw-ba-handle-line {
  position: absolute;
  inset: 0;
  width: 2px;
  margin: 0 auto;
  background: #fff;
}

.tw-ba-handle-circle {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: 40px;
  height: 40px;
  border-radius: 50%;
  background: #fff;
  display: flex;
  align-items: center;
  justify-content: center;
  box-shadow: 0 2px 6px rgba(0, 0, 0, 0.3);
}

The clip-path: inset(0 50% 0 0) starting value clips the "before" image at its horizontal midpoint (top right bottom left, so 50% from the right edge), which is what makes the slider start centered.

Step 3: JavaScript for Drag Behavior

function initBeforeAfterSlider(sliderId) {
  const slider = document.getElementById(sliderId);
  const beforeImage = slider.querySelector(".tw-ba-before");
  const handle = slider.querySelector(".tw-ba-handle");
  let dragging = false;

  function setPosition(clientX) {
    const rect = slider.getBoundingClientRect();
    let percent = ((clientX - rect.left) / rect.width) * 100;
    percent = Math.max(0, Math.min(100, percent));

    beforeImage.style.clipPath = `inset(0 ${100 - percent}% 0 0)`;
    handle.style.left = `${percent}%`;
  }

  slider.addEventListener("mousedown", () => (dragging = true));
  window.addEventListener("mouseup", () => (dragging = false));
  window.addEventListener("mousemove", (e) => {
    if (dragging) setPosition(e.clientX);
  });

  slider.addEventListener("touchstart", () => (dragging = true));
  window.addEventListener("touchend", () => (dragging = false));
  window.addEventListener(
    "touchmove",
    (e) => {
      if (dragging) setPosition(e.touches[0].clientX);
    },
    { passive: true },
  );

  // Also allow a direct click/tap to jump the handle to that position.
  slider.addEventListener("click", (e) => setPosition(e.clientX));
}

document.addEventListener("DOMContentLoaded", () => {
  initBeforeAfterSlider("ba-slider-1");
});

A few details that make this version actually work correctly rather than just look right at rest:

  • The clip calculation is 100 - percent, not percent, because clip-path: inset()'s second value (right) describes how much to clip away from the right edge, which is the inverse of how far the handle has moved from the left.
  • mousemove/touchmove are bound on window, not the slider itself. If they were bound only to the slider element, dragging past its edge (moving the mouse faster than the slider is wide) would stop updating the position the instant the cursor left the element's bounds.
  • { passive: true } on the touchmove listener tells the browser it's safe to start scrolling immediately without waiting on the handler, since this handler never calls preventDefault(). This keeps the page's normal scroll behavior smooth on mobile even while a drag is in progress elsewhere on the slider.

Step 4: Wrap It in a Shortcode

To make this reusable across posts and pages, wrap the markup in a shortcode that accepts the two image URLs as attributes:

add_shortcode( 'before_after', function ( $atts ) {
    static $instance = 0;
    $instance++;

    $atts = shortcode_atts( [
        'before' => '',
        'after'  => '',
    ], $atts, 'before_after' );

    if ( ! $atts['before'] || ! $atts['after'] ) {
        return '';
    }

    $id = 'ba-slider-' . $instance;

    ob_start();
    ?>
    <div class="tw-ba-slider" id="<?php echo esc_attr( $id ); ?>">
        <div class="tw-ba-image tw-ba-after">
            <img src="<?php echo esc_url( $atts['after'] ); ?>" alt="After">
        </div>
        <div class="tw-ba-image tw-ba-before">
            <img src="<?php echo esc_url( $atts['before'] ); ?>" alt="Before">
        </div>
        <div class="tw-ba-handle">
            <div class="tw-ba-handle-line"></div>
            <div class="tw-ba-handle-circle">&#8596;</div>
        </div>
    </div>
    <script>
        document.addEventListener('DOMContentLoaded', function () {
            initBeforeAfterSlider('<?php echo esc_js( $id ); ?>');
        });
    </script>
    <?php
    return ob_get_clean();
} );

The static $instance counter is what allows [before_after] to be used more than once on the same page without every slider sharing the same id and breaking each other's drag behavior. This is the same add_shortcode() pattern covered generally in building a custom WordPress shortcode, applied here to a two-image comparison widget instead of a simple button or highlight.

Important Details That Make or Break This Effect

  • Both images must share the exact same dimensions and framing. If the camera moved even slightly between the "before" and "after" shot, the comparison will look misaligned as the handle drags across it, since nothing is warping or matching the two images at render time.
  • Compress both images to a similar file size. A slider that loads a heavily compressed "before" photo next to an uncompressed "after" photo can make the after image look artificially sharper by contrast, which undermines the comparison's credibility.
  • Label which side is which. It sounds obvious, but a slider without a visible "Before"/"After" label is genuinely ambiguous to a visitor who lands mid-scroll without reading surrounding context.

Choosing Between the Two Approaches

  • Use Twenty20 Image Before-After if you want a maintained, tested drag interaction with zero custom code, and you're comfortable with one more plugin running on the site.
  • Build it yourself if you want the styling (handle design, labels, aspect ratio) to match your site exactly, or you already have a shortcode-driven image slider elsewhere on the site and want a consistent code style across both.

Frequently Asked Questions (FAQ) About Before-and-After Image Sliders

They should share the same pixel dimensions and framing for the comparison to look correct as the handle drags across it. If your source photos differ slightly, crop both to matching dimensions in an image editor before uploading, rather than relying on CSS to force a match.

Yes, as long as touch events are handled alongside mouse events, which both the Twenty20 plugin and the custom JavaScript shown above do. Test the drag gesture on an actual phone before publishing, since a slider that only responds to mouse movement will appear frozen on a touchscreen.

Yes, the technique works for any two images of the same subject at the same dimensions, screenshots of an old versus new website design, a "before editing" and "after editing" photo, or a floor plan comparison. The mechanism doesn't care what the images depict.

This almost always means the two images have different aspect ratios, or the container's aspect-ratio CSS property doesn't match the images' actual proportions. Recheck both source images are cropped identically and that the container's aspect-ratio value matches them.

Yes, with the shortcode version shown above, since each instance gets a unique id from the static counter. If you're hand-coding multiple sliders without the shortcode wrapper, make sure to give each one a distinct id and call initBeforeAfterSlider() separately for each.

clip-path with the inset() function is well supported in all modern browsers (Chrome, Firefox, Safari, Edge). The Twenty20 plugin's approach, which resizes an overlay image's width rather than using clip-path, is a viable fallback technique if you need to support unusually old browsers.

It helps significantly, especially for viewers who land on the image out of context, from a social share, for example. A short caption below the slider ("Kitchen renovation, 3 weeks") gives the comparison meaning beyond the visual alone.

Conclusion

A before-and-after slider is a small feature with an outsized visual impact, since it makes a transformation obvious in a single interaction rather than asking a visitor to mentally compare two separate photos. Twenty20 Image Before-After gets a tested, touch-friendly version running in a few minutes if you'd rather not maintain the drag logic yourself.

The custom clip-path implementation above is a real, complete alternative if you want the styling under your own control, and it demonstrates a technique, clipping one layered element based on pointer position, that's reusable for other interactive comparisons beyond just photos.

Whichever route you pick, get the source images right first: matching dimensions, matching framing, and similar compression. No amount of slider code fixes a comparison built on two photos that don't actually line up.

Tags :
Share :

Related Posts

Effortlessly Crafting Compelling WordPress Pages

Effortlessly Crafting Compelling WordPress Pages

As a website owner or content creator, having the ability to seamlessly add new pages to your WordPress site is crucial. Whether you're introducing a

Continue Reading
High Traffic Tips for WordPress Mastery 🚥

High Traffic Tips for WordPress Mastery 🚥

In our digital age, where online visibility is paramount, ensuring your WordPress site can handle surging traffic is crucial. Just like a finely-tune

Continue Reading
How Do I Change the WordPress Login URL?

How Do I Change the WordPress Login URL?

By default, every WordPress site's login page lives at the same predictable address: yoursite.com/wp-login.php (which also happens to redirect from

Continue Reading