A lightweight, customizable slider captcha for JavaScript, React, and TypeScript.
Supports UMD + ESM builds, themes, async verification, and server integration.
Perfect for adding a secure, interactive human verification step to your apps.
A live demo is available on GitHub Pages:
https://amazingdevteam.github.io/slider-captcha-js
- β‘ Works in Vanilla JS, React, and TypeScript
- π¦ UMD + ESM builds
- π¨ Customizable size, theme (
light/dark), and image source - π Built-in refresh button with hover effects
- π No backend required, but supports async
onVerifyandrequest()for server integration - πͺΆ Lightweight and dependency-free
- πΌοΈ Canvas fallback with gradient if image fails to load
- π§© Complex puzzle piece shape with shadows and outlines
- π« Auto-refresh after 3 failed attempts
Both the function API (sliderCaptcha) and the class API (new SliderCaptcha) accept an options object.
- If
request+onVerifyare provided β the captcha will use server-side validation. - Otherwise β it falls back to client-side validation (local puzzle check).
- If
imageUrlis provided β it will use that as the custom image source. - Otherwise β it falls back to a default random image (picsum).
| Option | Type | Default | Description |
|---|---|---|---|
root |
string / HTMLElement |
null |
Container element where captcha will render |
width |
number / string |
320 |
Width of the captcha |
height |
number |
160 |
Height of the captcha |
fit |
"cover" / "contain" / "stretch" |
"cover" |
How the image should fit |
imageUrl |
string / null |
null |
Custom image URL for the captcha background (overrides default/request) |
crossOrigin |
string / null |
null |
Cross-origin setting for images |
theme |
"light" / "dark" |
"light" |
Theme mode |
successText |
string |
"β
Verified!" |
Success message |
failText |
string |
"β Try again!" |
Failure message |
onSuccess |
function |
() => {} |
Callback when verification succeeds |
onFail |
function |
() => {} |
Callback when verification fails |
onRefresh |
function |
() => {} |
Callback when captcha is refreshed |
onVerify |
function |
null |
Optional async callback for custom verification |
request |
function |
null |
Optional async function returning { bgUrl, puzzleUrl } |
npm install slider-captcha-jsor via CDN:
<link rel="stylesheet" href="https://unpkg.com/slider-captcha-js/dist/slider-captcha.css" />
<script src="https://unpkg.com/slider-captcha-js/dist/slider-captcha.umd.js"></script>
<script>
const captcha = new SliderCaptcha({
root: "#stage",
width: 320,
height: 160,
onSuccess: () => alert("Verified!"),
onFail: () => alert("Try again"),
});
captcha.refresh();
</script><div id="stage"></div>
<script type="module">
import { SliderCaptcha } from "slider-captcha-js";
const captcha = new SliderCaptcha({
root: "#stage",
width: 320,
height: 160,
theme: "dark",
onSuccess: () => alert("Verified!"),
onFail: () => alert("Try again"),
});
captcha.refresh();
</script>import SliderCaptcha from "slider-captcha-js/react";
import { useRef } from "react";
function App() {
const captchaRef = useRef(null);
return (
<div>
<SliderCaptcha
ref={captchaRef}
width={320}
height={160}
theme="light"
onSuccess={() => console.log("Verified!")}
onFail={() => console.log("Try again")}
/>
<button onClick={() => captchaRef.current?.refresh()}>Refresh</button>
</div>
);
}import SliderCaptcha, { type SliderCaptchaRef } from "slider-captcha-js/react";
import { useRef } from "react";
function App() {
const captchaRef = useRef<SliderCaptchaRef>(null);
return (
<div>
<SliderCaptcha
ref={captchaRef}
width={320}
height={160}
theme="light"
onSuccess={() => console.log("Verified!")}
onFail={() => console.log("Try again")}
/>
<button onClick={() => captchaRef.current?.refresh()}>Refresh</button>
</div>
);
}import { SliderCaptcha } from "slider-captcha-js";
const captcha = new SliderCaptcha({
root: "#stage",
width: 320,
height: 160,
onVerify: async ({ x, duration, trail }) => {
// Custom async verification
if (Math.abs(x - 100) < 6) return true;
throw new Error("Verification failed");
},
});sliderCaptcha({
root: "#captcha",
request: async () => {
const res = await fetch("/api/captcha");
return res.json(); // { bgUrl, puzzleUrl }
},
onVerify: async (data) => {
const res = await fetch("/api/captcha/verify", {
method: "POST",
body: JSON.stringify(data),
});
const result = await res.json();
if (!result.success) throw new Error("Invalid");
},
});.my-dark-theme .slider-captcha-bar {
background: #222;
color: #eee;
}
.my-dark-theme .slider-captcha-refresh {
color: #0af;
}npm run buildOutputs:
dist/slider-captcha.umd.jsdist/slider-captcha.esm.jsdist/react-slider-captcha.jsdist/react-slider-captcha.esm.js
MIT Β© 2025