React component for the Redeyed Sentinel CAPTCHA — privacy-friendly bot detection that's free to use (you just need a free Site Key + Secret Key).
Get your keys at https://redeyed.com/developers.
npm i @redeyed_/sentinel-reactThis package ships TypeScript/TSX source under
src/. Most bundlers (Vite, Next.js, CRA, Webpack 5, esbuild) compile it for you out of the box. If your setup doesn't transpilenode_modules, add a small build step (e.g.tsc) that emits todist/and point your import there.
import { useState } from "react";
import { SentinelCaptcha } from "@redeyed_/sentinel-react";
export function SignupForm() {
const [token, setToken] = useState<string | null>(null);
async function handleSubmit(e: React.FormEvent) {
e.preventDefault();
// Send the token to YOUR server, which verifies it with your Secret Key.
await fetch("/api/signup", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ token, /* ...form fields */ }),
});
}
return (
<form onSubmit={handleSubmit}>
{/* ...your fields... */}
<SentinelCaptcha
siteKey={import.meta.env.VITE_SENTINEL_SITE_KEY}
onVerify={setToken}
onError={(err) => console.error(err)}
/>
<button type="submit" disabled={!token}>
Sign up
</button>
</form>
);
}If you'd rather pull the token from a hook than wire onVerify:
import { SentinelCaptcha, useSentinelToken } from "@redeyed_/sentinel-react";
function Widget() {
const token = useSentinelToken(); // latest token solved on the page
return <SentinelCaptcha siteKey="YOUR_SITE_KEY" />;
}| Prop | Type | Required | Description |
|---|---|---|---|
siteKey |
string |
yes | Public site key. If missing, renders nothing + console.warn. |
onVerify |
(token: string) => void |
no | Called with the verification token once solved. |
onError |
(error: Error) => void |
no | Called if the Sentinel script fails to load. |
widget |
string |
no | Widget variant (data-widget). |
theme |
string |
no | Theme name (data-theme). |
scheme |
string |
no | Color scheme (data-scheme): default, ocean, forest, sunset, graphite, royalty, ruby, hacker, monochrome, midnight, aurora. |
width |
string |
no | Widget width, e.g. full / 100% / 340px (data-width). |
difficulty |
string | number |
no | Challenge strength: easy/medium/hard/max or 1-6 (data-difficulty). |
baseUrl |
string |
no | Asset/script base URL. Defaults to https://redeyed.com. |
className |
string |
no | Extra class on the container (alongside sentinel-captcha). |
The token from onVerify only proves the widget was solved in the browser —
you must verify it server-side. Your Secret Key is secret and must never be
shipped to the browser.
POST https://redeyed.com/sentinel/siteverify
Content-Type: application/json
Accept: application/json
{ "secret": "<YOUR_SECRET_KEY>", "response": "<token-from-onVerify>" }You may also include an optional "remoteip" field with the end user's IP. The
check passes when the JSON response has success === true; the response also
carries outcome and score.
Both keys come from the Redeyed Lab → Sentinel → Sites. The Secret Key is shown once and stays server-side.
Using Next.js?
@redeyed_/sentinel-nextjsships a ready-madeverifySentinel()server helper.
- Add
widthprop (data-width) andmidnight/auroraschemes.
MIT © 2026 Redeyed Corporation