Open-source project
sneas/img-comparison-slider avatar
sneas/img-comparison-slider

img-comparison-slider: a web component for before and after images

Image comparison slider. Compare images before and after. Supports React, Vue, Angular.

873 stars58 forksTypeScriptMIT

At a glance

What is it?
img-comparison-slider is a small custom element that puts a draggable divider between two images so a reader can reveal one over the other, and it ships as a web component, a React package and a Vue package, with no framework required. The whole thing is under 12 kB minified, and every part of it, from the divider to the handle, is themed through CSS custom properties.
Who is it for?
img-comparison-slider suits a project that needs a before and after comparison in a marketing page, a documentation site or a bug report, and that wants to avoid writing the drag handling, the keyboard support and the focus management itself. It does not suit a case that needs a comparison grid, a zoom or a difference visualisation, because the component is a single divider over two images.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 11 days ago.
What is it written in?
Mainly TypeScript, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

A custom element, so the framework does not matter

img-comparison-slider is a slider component for comparing images before and after, and the decision that shapes everything else is that it is a web component rather than a library for one framework. The README lists the supported frameworks as React, Angular, Vue 2 and Vue 3, with a separate package for React and for Vue 3 and installation pages for Angular and Vue 2, but the core is a custom element you can use in plain HTML. The badge for publication on webcomponents.org is the marker for that. The feature list is short and specific: mobile friendly, accessible, responsive, compact at less than 12 kB minified or less than 4 kB gzipped, compatible with modern frameworks, and distributed via a content delivery network or through npm. The primary language is TypeScript, the licence is MIT, and the last push was on 2026-10-12.

Using it in plain HTML is a script tag and two images

The HTML usage is the shortest path and it shows the whole interface. The script and the stylesheet come from a content delivery network, and the element takes two images assigned to slots named first and second:

html
<script
  defer
  src="https://cdn.jsdelivr.net/npm/img-comparison-slider@8/dist/index.js"
></script>
<link
  rel="stylesheet"
  href="https://cdn.jsdelivr.net/npm/img-comparison-slider@8/dist/styles.css"
/>

<img-comparison-slider>
  <img slot="first" src="before.jpg" />
  <img slot="second" src="after.jpg" />
</img-comparison-slider>

The slot names are the API for which image is which, and the element itself carries the dimensions of the comparison. The script is deferred, so it does not block the page, and the stylesheet is a separate file, so the element renders with its default appearance before the script upgrades it. The examples page collects usage examples, and the example image in the readme is an animated capture of the interaction.

Five attributes, and the ones that change the behaviour

Beyond the default HTMLElement attributes such as class, tabindex and title, the component supports five of its own. The value attribute is the position of the divider in percent, defaulting to 50 and ranging from 0 to 100, and it is readable and settable, so the current state can be read or driven from code. The hover attribute makes the slider slide automatically on mouse over, and it defaults to false. The direction attribute sets whether the divider runs horizontally or vertically, with horizontal as the default. The keyboard attribute enables or disables slider position control with the keyboard, and it defaults to enabled, which is the accessibility story in one line. The handle attribute enables or disables dragging by handle only, defaulting to false, which means that by default you can drag anywhere on the divider rather than only on the handle itself. The component also emits a slide event when the slider position is changed by the user, so you can react to it.

Every visual detail is a CSS custom property

The styling approach is to expose the component's appearance as CSS variables, and the table of them is short enough to be the whole theming story. The divider variables are the width of the line separating the two images, its colour and its shadow. The handle variables are its position along the divider axis, which can be displaced off the centre, its width, its colour, its opacity and its shadow. The example in the readme shows the pattern, a style block targeting the element and setting three of them:

html
<style type="text/css">
  img-comparison-slider {
    --divider-width: 2px;
    --divider-color: #c0c0c0;
    --default-handle-opacity: 0.3;
  }
</style>

The defaults are a 1px white divider with no shadow and a fully opaque 50px white handle, and the example values show what overriding looks like in practice, a 2px grey divider and a handle at 0.3 opacity so it does not obscure the image behind it. The handle itself can be replaced entirely, by assigning the attribute slot="handle" to any element inside the component, which is how you would use an SVG grip instead of the built-in one.

Browser requirements are the real constraint

The component relies on three platform features, and the readme names each with a polyfill pointer. The first is ECMAScript 2015, with Babel if you need it. The second is Custom Elements version 1 and Shadow DOM version 1, with the webcomponents.org polyfills for older browsers. The third is CSS variables, that is custom properties, with a specific polyfill for IE11. The Shadow DOM part matters more than it looks, because it is what keeps the component's internal elements out of the page's style scope, and it is why the divider and the handle are themed with custom properties rather than with class names, since a shadow root does not inherit the page's stylesheets. So the component is compact partly because it does not need to defend its internals from the host page, and the cost is that older browsers need the polyfills loaded.

A monorepo with framework packages and a docs site

The repository is an npm workspace with a packages directory and the pattern is visible in the root manifest. The root package is the project, private, with a version of 0.0.0-auto because the release tooling sets it, and the workspaces field points at packages/* so every package there is part of the same install. The dev dependencies tell you the build story. The component itself is built with rollup, with plugins for node resolution, commonjs, terser and typescript2, and the site is built with webpack, with loaders for css, sass, html and typescript, plus the html bundler plugin, the copy plugin and the dev server. Semantic-release with the monorepo plugin handles the versioning, which is why the tags carry a package scope and a version, such as the core package at 8.0.7 and the React package at 8.0.2. The start scripts run the component and the site in parallel with npm-run-all, and the repository also has a docs directory for the installation guides, a scripts directory, and CircleCI and husky with lint-staged for the commit hooks.

Editorial conclusion

img-comparison-slider suits a project that needs a before and after comparison in a marketing page, a documentation site or a bug report, and that wants to avoid writing the drag handling, the keyboard support and the focus management itself. It does not suit a case that needs a comparison grid, a zoom or a difference visualisation, because the component is a single divider over two images. Before you use it, read the attribute table and check the ones you need are there, and if you are targeting older browsers, note the browser requirements list, since the component relies on custom elements, shadow DOM and CSS custom properties, all of which need polyfills for older browsers.

Frequently asked questions

How do I use img-comparison-slider with plain HTML?

Include the deferred script and the stylesheet from a content delivery network, then add the element with two images assigned to the first and second slots. The component is a custom element, so no framework is required, and separate React, Vue and Angular packages are published for those who want them.

Which attributes does img-comparison-slider support?

Five beyond the standard HTMLElement ones. The value attribute sets the divider position in percent from 0 to 100 and defaults to 50, hover slides automatically on mouse over and defaults to false, direction is horizontal or vertical, keyboard enables or disables keyboard control of the position and is enabled by default, and handle restricts dragging to the handle alone and defaults to false.

How do I restyle the divider and the handle?

With CSS custom properties set on the element, such as the divider width, colour and shadow and the handle position, width, colour, opacity and shadow. The handle itself can be replaced by putting slot="handle" on any element inside the component.

What browsers does img-comparison-slider need?

ECMAScript 2015, Custom Elements version 1 and Shadow DOM version 1, and CSS custom properties. The readme points at Babel, the webcomponents.org polyfills and an IE11 custom properties polyfill for older browsers, and it notes that Shadow DOM is what keeps the component's internals out of the page's style scope.

Official sources

  1. License: MIT
  2. Project website
  3. README
  4. Releases
  5. sneas/img-comparison-slider on GitHub
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/sneas-img-comparison-slider.svg)](https://hysenlabs.com/projects/sneas-img-comparison-slider)

Was this useful?