i try built a lightweight alternative captcha for react/nextjs π
i recently wanted to add a bit of friction to my forms just to slow down automated bots. i didn't need anything massiveβjust a simple hurdle to make it a little more "expensive" or annoying for bots to spam.
so i built react-math-captcha. it's just a lightweight, self-hosted tool that does exactly that without overcomplicating things.
the simple goal
this isn't trying to be the most "advanced" security layer on the planet. the goal is just to make it more tedious for basic automated scripts to bypass your forms.
- simple integration: easy to drop into existing React/Next.js projects.
-
no database needed: uses encrypted JWE sessions (via
jose), so you don't need Redis or a DB to store captcha state. - ssr ready: uses SVGs, so it works perfectly with Next.js and other SSR frameworks.
how to use it
it's a quick setup if you're already in a React environment.
- set the secret
add this to your
.env. this secret is used to encrypt the JWE session, keeping all captcha states secure without needing a database or Redis.
CAPTCHA_SECRET=at-least-16-characters-please
2. install
npm install react-math-captcha
3. add the route (Next.js)
create an API route to act as the "brain" of the captcha. this handler manages the gatekeeper (slider) and generates the SVG math challenges to keep things lightweight on the client.
// app/api/captcha/route.ts
import { createCaptchaHandler } from 'react-math-captcha/server'
export const { POST } = createCaptchaHandler()
4. use the component
mount the widget in your form. users will be prompted to slide a slider (gatekeeper) before receiving the math challenge.
'use client'
import { MathCaptcha } from 'react-math-captcha/react'
import { register } from './actions'
export default function Page() {
return (
<form action={register}>
<input name="email" placeholder="email" />
<MathCaptcha />
<button>submit</button>
</form>
)
}
5. verify on the server
finally, verify the token on the server. if the answer is correct, the system provides a verificationToken. once verifyCaptchaToken is called, the token is consumed immediately (one-time use) to prevent replay attacks by bots.
// actions.ts
'use server'
import { verifyCaptchaToken } from 'react-math-captcha/server'
export async function register(formData: FormData) {
try {
await verifyCaptchaToken(formData)
// captcha is valid & token consumed (secure from replay attacks)
} catch (err) {
// invalid captcha
return { error: 'invalid captcha' }
}
}
keeping it simple
at the end of the day, sometimes you just need a small barrier to keep things clean. if you're looking for a simple way to add some bot friction, feel free to check out the repo.
GitHub: tinoimammp/react-math-captcha
hope this helps someone!
Top comments (0)