Why React Apps Need Bot Detection
React applications are prime targets for bot attacks. Your forms collect user data, submissions trigger backend actions, and API calls expose sensitive endpoints. Without bot detection, attackers can:
- Scrape user data from forms and API endpoints
- Spam your user-generated content platforms
- Brute-force login credentials at scale
- Perform credential-stuffing attacks on checkout flows
- Abuse your API with fake account registrations or purchases
React runs entirely in the browser, which means your forms and API calls are vulnerable from the start. Unlike Next.js apps with backend middleware, React apps (especially Create React App or Vite-based apps) lack native server-side bot detection. This guide shows you how to add real bot detection to React—with working code for hooks, form protection, API proxy patterns, and server-side verification.
What You'll Need
- A React project (Create React App, Vite, Next.js, or any bundler)
- A free device.ai API key (1,000 daily verifications): visit device.ai
- A simple Node backend or API route for server-side verification (can be Express, Fastify, or Next.js API routes)
- Five minutes to integrate
How Bot Detection Works in React
Bot detection happens in two phases:
Phase 1: Client-Side Signal Collection
When a user interacts with your React app, device.ai's JavaScript SDK collects behavioral signals:
- Device fingerprinting: Canvas rendering, WebGL, screen resolution, browser capabilities
- Automation detection: Detects Puppeteer, Selenium, Playwright, and other bots
- Behavioral signals: Mouse movement patterns, keystroke timing, scroll behavior
These signals are compressed and sent to your backend (not directly to device.ai's API, for security).
Phase 2: Server-Side Verification
Your backend receives the device signals and calls device.ai's verification endpoint with your API key. device.ai returns a risk score (0.0 to 1.0):
- 0.0-0.3: High confidence bot. Block immediately.
- 0.3-0.6: Uncertain. Challenge with CAPTCHA or require additional verification.
- 0.6-1.0: Likely human. Allow.
Your backend decides what to do with the score: block, challenge, rate-limit, or allow.
Step 1: Get Your API Key (1 minute)
Visit device.ai, click "Get Free API Key", and copy your key. No signup. No credit card. Your first 1,000 daily verifications are free.
Add it to your backend's environment variables:
DEVICE_AI_API_KEY=dv_live_your_key_here
Step 2: Add the Client-Side SDK to React
Add device.ai's JavaScript SDK to your React app's root HTML file (typically public/index.html for Create React App):
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<!-- Add device.ai SDK -->
<script src="https://js.device.ai/v1/device.js"></script>
</head>
<body>
<div id="root"></div>
</body>
</html>
The device.ai SDK will automatically initialize and start collecting behavioral signals the moment your page loads.
Step 3: Create a useDeviceAI Hook
Create a reusable React hook to collect device signals and send them to your backend for verification:
// hooks/useDeviceAI.ts
import { useCallback, useState } from 'react';
interface VerifyResult {
score: number; // 0.0 (bot) to 1.0 (human)
confidence: number; // 0.0 to 1.0
risk_level: 'low' | 'medium' | 'high';
is_bot: boolean;
}
interface UseDeviceAIOptions {
apiEndpoint?: string; // Your backend endpoint
onVerify?: (result: VerifyResult) => void;
onError?: (error: Error) => void;
}
export function useDeviceAI(options: UseDeviceAIOptions = {}) {
const {
apiEndpoint = '/api/verify-device',
onVerify,
onError,
} = options;
const [isLoading, setIsLoading] = useState(false);
const [error, setError] = useState(null);
const [result, setResult] = useState(null);
const verify = useCallback(async () => {
setIsLoading(true);
setError(null);
try {
// Get device signals from window.DeviceAI (injected by the SDK)
const signals = (window as any).DeviceAI?.getSignals?.() || {};
// Call your backend verification endpoint
const response = await fetch(apiEndpoint, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ signals }),
});
if (!response.ok) {
throw new Error(
`Verification failed: ${response.status} ${response.statusText}`
);
}
const verifyResult: VerifyResult = await response.json();
setResult(verifyResult);
onVerify?.(verifyResult);
return verifyResult;
} catch (err) {
const error = err instanceof Error ? err : new Error(String(err));
setError(error);
onError?.(error);
throw error;
} finally {
setIsLoading(false);
}
}, [apiEndpoint, onVerify, onError]);
return {
verify,
isLoading,
error,
result,
isBot: result ? result.score < 0.3 : null,
isHuman: result ? result.score > 0.6 : null,
};
}
Step 4: Protect a React Form
Use the hook to protect a form submission:
// components/LoginForm.tsx
import { useState } from 'react';
import { useDeviceAI } from '../hooks/useDeviceAI';
export function LoginForm() {
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const [message, setMessage] = useState(null);
const [messageType, setMessageType] = useState<'error' | 'success' | null>(null);
const { verify, isLoading } = useDeviceAI({
apiEndpoint: '/api/verify-device',
onVerify: (result) => {
if (result.score < 0.3) {
setMessage('Bot detected. Please try again.');
setMessageType('error');
} else if (result.score < 0.6) {
setMessage('Please complete the verification challenge.');
setMessageType('error');
} else {
// Bot check passed, proceed with login
submitLogin(email, password);
}
},
onError: (error) => {
console.error('Device verification failed:', error);
setMessage('Verification failed. Please try again.');
setMessageType('error');
},
});
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
if (!email || !password) {
setMessage('Please fill in all fields.');
setMessageType('error');
return;
}
// Step 1: Verify device before submitting login
await verify();
};
const submitLogin = async (email: string, password: string) => {
try {
const response = await fetch('/api/login', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ email, password }),
});
if (!response.ok) {
const error = await response.json();
setMessage(error.message || 'Login failed.');
setMessageType('error');
return;
}
const result = await response.json();
setMessage('Login successful!');
setMessageType('success');
// Redirect or update app state
} catch (error) {
setMessage('An error occurred. Please try again.');
setMessageType('error');
}
};
return (
);
}
Step 5: Build Your Backend Verification Endpoint
Your React app can't directly call device.ai's API (because you can't expose your API key in the browser). Instead, create a backend endpoint that does the verification for you. This is the **proxy pattern** or **Backend for Frontend (BFF)**.
Option A: Express Backend
// backend/routes/verify-device.ts
import express from 'express';
const router = express.Router();
router.post('/api/verify-device', async (req, res) => {
const { signals } = req.body;
if (!signals) {
return res.status(400).json({ error: 'Missing device signals' });
}
try {
// Call device.ai's verification endpoint from your backend
const verifyResponse = await fetch('https://device.ai/v1/verify', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${process.env.DEVICE_AI_API_KEY}`,
},
body: JSON.stringify({
signals,
// Optional: include IP for additional signals
ip: req.ip,
}),
});
if (!verifyResponse.ok) {
throw new Error(
`device.ai verification failed: ${verifyResponse.status}`
);
}
const result = await verifyResponse.json();
return res.json({
score: result.score,
confidence: result.confidence,
risk_level:
result.score < 0.3 ? 'high' : result.score < 0.6 ? 'medium' : 'low',
is_bot: result.score < 0.5, // Adjust threshold as needed
});
} catch (error) {
console.error('Device verification error:', error);
// Fail open: return neutral score instead of blocking
return res.json({
score: 0.5,
confidence: 0.2,
risk_level: 'medium',
is_bot: false,
});
}
});
export default router;
Option B: Next.js API Route (if using Next.js)
// pages/api/verify-device.ts
import type { NextApiRequest, NextApiResponse } from 'next';
type ResponseData = {
score: number;
confidence: number;
risk_level: 'low' | 'medium' | 'high';
is_bot: boolean;
};
export default async function handler(
req: NextApiRequest,
res: NextApiResponse
) {
if (req.method !== 'POST') {
return res.status(405).json({
score: 0.5,
confidence: 0,
risk_level: 'medium',
is_bot: false,
});
}
const { signals } = req.body;
if (!signals) {
return res.status(400).json({
score: 0.5,
confidence: 0,
risk_level: 'medium',
is_bot: false,
});
}
try {
const verifyResponse = await fetch('https://device.ai/v1/verify', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${process.env.DEVICE_AI_API_KEY}`,
},
body: JSON.stringify({
signals,
ip: req.headers['x-forwarded-for'] || req.socket.remoteAddress,
}),
});
if (!verifyResponse.ok) {
throw new Error('device.ai verification failed');
}
const result = await verifyResponse.json();
return res.json({
score: result.score,
confidence: result.confidence,
risk_level:
result.score < 0.3 ? 'high' : result.score < 0.6 ? 'medium' : 'low',
is_bot: result.score < 0.5,
});
} catch (error) {
console.error('Device verification error:', error);
// Fail open
return res.json({
score: 0.5,
confidence: 0.2,
risk_level: 'medium',
is_bot: false,
});
}
}
Step 6: Protect API Calls with a Custom Fetch Wrapper
Create a wrapper around fetch to automatically verify devices before making API calls:
// utils/verified-fetch.ts
import { useDeviceAI } from '../hooks/useDeviceAI';
export async function verifiedFetch(
url: string,
options: RequestInit = {}
) {
// First, verify the device
const response = await fetch('/api/verify-device', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ signals: {} }),
});
if (!response.ok) {
throw new Error('Device verification failed');
}
const verification = await response.json();
if (verification.score < 0.3) {
throw new Error('Bot detected. Request blocked.');
}
if (verification.score < 0.6) {
throw new Error('Device verification required. Please try again.');
}
// If verification passed, make the actual API call
return fetch(url, options);
}
Step 7: Add Bot Detection to Checkout Forms
Checkout flows are high-value targets for bots. Protect them with bot detection:
// components/CheckoutForm.tsx
import { useState } from 'react';
import { useDeviceAI } from '../hooks/useDeviceAI';
export function CheckoutForm() {
const [items, setItems] = useState([]); // Your cart items
const [isProcessing, setIsProcessing] = useState(false);
const [error, setError] = useState(null);
const { verify, isLoading } = useDeviceAI({
apiEndpoint: '/api/verify-device',
onVerify: (result) => {
if (result.score < 0.3) {
setError('Bot detected. Please try again later.');
} else if (result.score < 0.6) {
setError('Please complete the verification challenge.');
} else {
processCheckout();
}
},
onError: (error) => {
setError(`Verification failed: ${error.message}`);
},
});
const handleCheckout = async (e: React.FormEvent) => {
e.preventDefault();
await verify();
};
const processCheckout = async () => {
setIsProcessing(true);
try {
const response = await fetch('/api/checkout', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ items }),
});
if (!response.ok) {
setError('Checkout failed. Please try again.');
return;
}
const result = await response.json();
// Redirect to payment or show success
} catch (err) {
setError('An error occurred. Please try again.');
} finally {
setIsProcessing(false);
}
};
return (
);
}
Complete Threshold Guide
Here's how to interpret device.ai scores for different use cases:
| Use Case | Block Threshold | Challenge Threshold | Allow Threshold |
|---|---|---|---|
| Login / Account Takeover | < 0.3 | 0.3 - 0.6 | > 0.6 |
| Form Submission (Comments, Reviews) | < 0.2 | 0.2 - 0.5 | > 0.5 |
| Checkout / Payment | < 0.4 | 0.4 - 0.7 | > 0.7 |
| API Rate Limiting | < 0.5 | N/A | > 0.5 |
| Signup / Account Creation | < 0.25 | 0.25 - 0.55 | > 0.55 |
Error Handling and Fallback Strategies
Bot detection should never break your app. Always have fallback strategies:
// Fail-open pattern: if device.ai is down, allow the request
const verifyResponse = await fetch('https://device.ai/v1/verify', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${process.env.DEVICE_AI_API_KEY}`,
},
body: JSON.stringify({ signals }),
}).catch(() => {
// Network error or timeout: fail open
return new Response(
JSON.stringify({ score: 0.7, confidence: 0, is_bot: false }),
{ status: 200 }
);
});
Monitoring and Metrics
Track these metrics to understand your bot traffic:
- Bot block rate: Percentage of requests with score < 0.3
- Challenge rate: Percentage of requests with score 0.3-0.6
- Allow rate: Percentage of requests with score > 0.6
- Conversion impact: Does bot detection affect legitimate user conversion?
- False positive rate: Do real users get blocked/challenged?
Log these metrics to your analytics provider or Slack for visibility.
Best Practices for React Bot Detection
- Never expose your API key in the browser. Always use a backend proxy (Express, Next.js, or any server).
- Verify once per action, not per keystroke. Excessive verification adds latency and consumes your API quota.
- Use adaptive thresholds. Checkout flows can be stricter (0.4 threshold) than comments (0.2 threshold).
- Monitor false positives. If legitimate users are being challenged or blocked, adjust thresholds.
- Fail gracefully. If device.ai is unavailable, allow the request and log the incident.
- Combine with other signals. Bot detection is one layer. Use rate limiting, email verification, and CAPTCHAs for defense-in-depth.
- Test with automated tools. Use Puppeteer or Playwright to verify that your bot detection actually catches bots.
Frequently Asked Questions
Can I Call device.ai Directly from React?
No. Calling device.ai's API directly from your browser would require exposing your API key, which is a security vulnerability. Always use a backend proxy—it's one extra API call and protects your credentials.
What If I'm Using Vite or a Different Bundler?
The device.ai JavaScript SDK works with any bundler. Just add the <script> tag to your HTML or import the SDK via npm if available. The hook pattern and backend verification logic remain the same.
Can I Use This with React Native or Expo?
Yes. Instead of the browser SDK, use device.ai's mobile API directly from your React Native app. The verification endpoint on your backend remains the same—only the client-side signal collection changes to use native APIs.
How Much Does device.ai Cost?
Free tier: 1,000 verifications per day at no cost. Paid: $0.001 per verification. For 1 million daily verifications, you'd pay ~$300/month.
Does device.ai Support CORS?
Your backend handles the device.ai call, so CORS isn't an issue. Your React app calls your backend (same origin or configured CORS), and your backend calls device.ai.
Next Steps
- Get a free API key at device.ai
- Copy the
useDeviceAIhook into your React project - Create your backend verification endpoint (Express or Next.js)
- Add device verification to your first form
- Test with Puppeteer or a browser automation tool to verify it works
- Monitor bot detection metrics and adjust thresholds based on your false positive rate
Resources
- device.ai API Documentation — Complete API reference and integration guides
- Bot Detection for Next.js — Framework Guide #1 for Next.js apps
- What Is Bot Detection? Complete Guide for Developers — Understand bot detection concepts and methodology
Questions? Contact support@device.ai or check our docs.