roCaptcha is a privacy-preserving CAPTCHA library for React applications. It provides a decentralized approach to bot protection while maintaining user privacy. This guide walks through setting up and creating CAPTCHA protection using ProCaptcha with React, from installation to a working implementation. This is part 56 of a series on using ProCaptcha with React.
Prerequisites
CAPTCHA Types
Before you begin, make sure you have:
- Node.js version 14.0 or higher installed
- npm, yarn, or pnpm package manager
- A React project (version 16.8 or higher) or create-react-app setup
- Basic knowledge of React hooks (useState, useEffect)
- Familiarity with JavaScript/TypeScript
- Understanding of async operations
Installation
Install ProCaptcha using your preferred package manager:
npm install @prosopo/captcha
Or with yarn:
yarn add @prosopo/captcha
Or with pnpm:
pnpm add @prosopo/captcha
After installation, your package.json should include:
{
"dependencies": {
"@prosopo/captcha": "^1.0.0",
"react": "^18.0.0",
"react-dom": "^18.0.0"
}
}
Project Setup
ProCaptcha requires configuration. Set up the provider and component:
// src/index.js
import React from 'react';
import ReactDOM from 'react-dom/client';
import { ProsopoProvider } from '@prosopo/captcha';
import App from './App';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<React.StrictMode>
<ProsopoProvider
config={{
dappName: 'MyApp',
networkId: 'development'
}}
>
<App />
</ProsopoProvider>
</React.StrictMode>
);
First Example / Basic Usage
Let's create a simple CAPTCHA form. Create a new file src/CaptchaExample.jsx:
// src/CaptchaExample.jsx
import React, { useState } from 'react';
import { useProsopo } from '@prosopo/captcha';
function CaptchaExample() {
const { captcha, verify } = useProsopo();
const [isVerified, setIsVerified] = useState(false);
const [isLoading, setIsLoading] = useState(false);
const handleVerify = async () => {
setIsLoading(true);
try {
const result = await captcha.verify();
setIsVerified(result);
if (result) {
alert('CAPTCHA verified successfully!');
} else {
alert('CAPTCHA verification failed. Please try again.');
}
} catch (error) {
console.error('CAPTCHA error:', error);
alert('Error verifying CAPTCHA. Please try again.');
} finally {
setIsLoading(false);
}
};
return (
<div style={{ padding: '20px', maxWidth: '400px', margin: '0 auto' }}>
<h2>ProCaptcha Example</h2>
<div style={{ marginBottom: '20px', padding: '20px', border: '1px solid #ddd', borderRadius: '8px' }}>
{captcha && (
<div>
<p>Complete the CAPTCHA challenge below:</p>
<div style={{ marginTop: '15px' }}>
{captcha.render()}
</div>
</div>
)}
</div>
<button
onClick={handleVerify}
disabled={isLoading}
style={{
padding: '10px 20px',
backgroundColor: isLoading ? '#6c757d' : '#007bff',
color: 'white',
border: 'none',
borderRadius: '4px',
cursor: isLoading ? 'not-allowed' : 'pointer',
width: '100%'
}}
>
{isLoading ? 'Verifying...' : 'Verify CAPTCHA'}
</button>
{isVerified && (
<p style={{ color: 'green', marginTop: '10px', textAlign: 'center' }}>
✓ CAPTCHA verified successfully!
</p>
)}
</div>
);
}
export default CaptchaExample;
Update your App.jsx:
// src/App.jsx
import React from 'react';
import CaptchaExample from './CaptchaExample';
import './App.css';
function App() {
return (
<div className="App">
<CaptchaExample />
</div>
);
}
export default App;
This creates a basic CAPTCHA form that verifies user interaction.
Understanding the Basics
ProCaptcha provides several key features:
- ProsopoProvider: Context provider for CAPTCHA functionality
- useProsopo hook: Access CAPTCHA methods and state
- Privacy-preserving: Decentralized approach to bot protection
- Verification: Async verification process
- Customizable: Configurable for different networks and dapps
Key concepts:
- Provider Setup: Wrap your app with ProsopoProvider
-
Hook Usage: Use
useProsopohook to access CAPTCHA -
Verification: Call
captcha.verify()to verify user interaction - State Management: Track verification state in your component
- Error Handling: Handle verification errors gracefully
Here's an example with form integration:
// src/FormWithCaptcha.jsx
import React, { useState } from 'react';
import { useProsopo } from '@prosopo/captcha';
function FormWithCaptcha() {
const { captcha, verify } = useProsopo();
const [formData, setFormData] = useState({
email: '',
message: ''
});
const [isVerified, setIsVerified] = useState(false);
const [isSubmitting, setIsSubmitting] = useState(false);
const handleInputChange = (e) => {
const { name, value } = e.target;
setFormData(prev => ({ ...prev, [name]: value }));
};
const handleVerify = async () => {
try {
const result = await verify();
setIsVerified(result);
} catch (error) {
console.error('Verification error:', error);
}
};
const handleSubmit = async (e) => {
e.preventDefault();
if (!isVerified) {
alert('Please complete the CAPTCHA first.');
return;
}
setIsSubmitting(true);
// Simulate form submission
await new Promise(resolve => setTimeout(resolve, 1000));
alert('Form submitted successfully!');
setIsSubmitting(false);
};
return (
<div style={{ padding: '20px', maxWidth: '500px', margin: '0 auto' }}>
<h2>Form with ProCaptcha</h2>
<form onSubmit={handleSubmit}>
<div style={{ marginBottom: '16px' }}>
<label style={{ display: 'block', marginBottom: '4px' }}>
Email:
</label>
<input
type="email"
name="email"
value={formData.email}
onChange={handleInputChange}
required
style={{
width: '100%',
padding: '8px',
border: '1px solid #ddd',
borderRadius: '4px',
boxSizing: 'border-box'
}}
/>
</div>
<div style={{ marginBottom: '16px' }}>
<label style={{ display: 'block', marginBottom: '4px' }}>
Message:
</label>
<textarea
name="message"
value={formData.message}
onChange={handleInputChange}
required
rows={4}
style={{
width: '100%',
padding: '8px',
border: '1px solid #ddd',
borderRadius: '4px',
boxSizing: 'border-box',
resize: 'vertical'
}}
/>
</div>
<div style={{ marginBottom: '20px', padding: '15px', border: '1px solid #ddd', borderRadius: '4px', backgroundColor: '#f8f9fa' }}>
<label style={{ display: 'block', marginBottom: '10px' }}>
CAPTCHA Verification:
</label>
{captcha && (
<div style={{ marginBottom: '10px' }}>
{captcha.render()}
</div>
)}
<button
type="button"
onClick={handleVerify}
style={{
padding: '8px 16px',
backgroundColor: '#28a745',
color: 'white',
border: 'none',
borderRadius: '4px',
cursor: 'pointer'
}}
>
Verify CAPTCHA
</button>
{isVerified && (
<p style={{ color: 'green', marginTop: '10px' }}>
✓ Verified
</p>
)}
</div>
<button
type="submit"
disabled={!isVerified || isSubmitting}
style={{
padding: '10px 20px',
backgroundColor: (!isVerified || isSubmitting) ? '#6c757d' : '#007bff',
color: 'white',
border: 'none',
borderRadius: '4px',
cursor: (!isVerified || isSubmitting) ? 'not-allowed' : 'pointer',
width: '100%'
}}
>
{isSubmitting ? 'Submitting...' : 'Submit'}
</button>
</form>
</div>
);
}
export default FormWithCaptcha;
Practical Example / Building Something Real
Let's build a complete contact form with ProCaptcha protection:
// src/ContactFormWithProCaptcha.jsx
import React, { useState } from 'react';
import { useProsopo } from '@prosopo/captcha';
function ContactFormWithProCaptcha() {
const { captcha, verify } = useProsopo();
const [formData, setFormData] = useState({
name: '',
email: '',
subject: '',
message: ''
});
const [isVerified, setIsVerified] = useState(false);
const [isVerifying, setIsVerifying] = useState(false);
const [isSubmitting, setIsSubmitting] = useState(false);
const [error, setError] = useState('');
const handleInputChange = (e) => {
const { name, value } = e.target;
setFormData(prev => ({ ...prev, [name]: value }));
setError('');
};
const handleVerify = async () => {
setIsVerifying(true);
setError('');
try {
const result = await verify();
setIsVerified(result);
if (!result) {
setError('CAPTCHA verification failed. Please try again.');
}
} catch (err) {
setError('Error verifying CAPTCHA. Please try again.');
console.error('Verification error:', err);
} finally {
setIsVerifying(false);
}
};
const handleSubmit = async (e) => {
e.preventDefault();
setError('');
if (!isVerified) {
setError('Please complete the CAPTCHA verification first.');
return;
}
setIsSubmitting(true);
try {
// Simulate API call
await new Promise(resolve => setTimeout(resolve, 1500));
alert('Form submitted successfully!');
// Reset form
setFormData({ name: '', email: '', subject: '', message: '' });
setIsVerified(false);
} catch (err) {
setError('Error submitting form. Please try again.');
} finally {
setIsSubmitting(false);
}
};
return (
<div style={{ padding: '20px', maxWidth: '600px', margin: '0 auto' }}>
<h1>Contact Form with ProCaptcha</h1>
<form onSubmit={handleSubmit}>
<div style={{ marginBottom: '16px' }}>
<label style={{ display: 'block', marginBottom: '4px', fontWeight: '500' }}>
Name *
</label>
<input
type="text"
name="name"
value={formData.name}
onChange={handleInputChange}
required
style={{
width: '100%',
padding: '8px',
border: '1px solid #ddd',
borderRadius: '4px',
boxSizing: 'border-box'
}}
/>
</div>
<div style={{ marginBottom: '16px' }}>
<label style={{ display: 'block', marginBottom: '4px', fontWeight: '500' }}>
Email *
</label>
<input
type="email"
name="email"
value={formData.email}
onChange={handleInputChange}
required
style={{
width: '100%',
padding: '8px',
border: '1px solid #ddd',
borderRadius: '4px',
boxSizing: 'border-box'
}}
/>
</div>
<div style={{ marginBottom: '16px' }}>
<label style={{ display: 'block', marginBottom: '4px', fontWeight: '500' }}>
Subject *
</label>
<input
type="text"
name="subject"
value={formData.subject}
onChange={handleInputChange}
required
style={{
width: '100%',
padding: '8px',
border: '1px solid #ddd',
borderRadius: '4px',
boxSizing: 'border-box'
}}
/>
</div>
<div style={{ marginBottom: '16px' }}>
<label style={{ display: 'block', marginBottom: '4px', fontWeight: '500' }}>
Message *
</label>
<textarea
name="message"
value={formData.message}
onChange={handleInputChange}
required
rows={5}
style={{
width: '100%',
padding: '8px',
border: '1px solid #ddd',
borderRadius: '4px',
boxSizing: 'border-box',
resize: 'vertical'
}}
/>
</div>
<div style={{ marginBottom: '20px', padding: '15px', border: '1px solid #ddd', borderRadius: '4px', backgroundColor: '#f8f9fa' }}>
<label style={{ display: 'block', marginBottom: '10px', fontWeight: '500' }}>
CAPTCHA Verification *
</label>
{captcha && (
<div style={{ marginBottom: '10px' }}>
{captcha.render()}
</div>
)}
<button
type="button"
onClick={handleVerify}
disabled={isVerifying || isVerified}
style={{
padding: '8px 16px',
backgroundColor: (isVerifying || isVerified) ? '#6c757d' : '#28a745',
color: 'white',
border: 'none',
borderRadius: '4px',
cursor: (isVerifying || isVerified) ? 'not-allowed' : 'pointer'
}}
>
{isVerifying ? 'Verifying...' : isVerified ? '✓ Verified' : 'Verify CAPTCHA'}
</button>
</div>
{error && (
<div style={{
padding: '10px',
backgroundColor: '#f8d7da',
color: '#721c24',
borderRadius: '4px',
marginBottom: '16px'
}}>
{error}
</div>
)}
<button
type="submit"
disabled={!isVerified || isSubmitting}
style={{
padding: '10px 20px',
backgroundColor: (!isVerified || isSubmitting) ? '#6c757d' : '#007bff',
color: 'white',
border: 'none',
borderRadius: '4px',
cursor: (!isVerified || isSubmitting) ? 'not-allowed' : 'pointer',
width: '100%',
fontSize: '16px'
}}
>
{isSubmitting ? 'Submitting...' : 'Submit Form'}
</button>
</form>
</div>
);
}
export default ContactFormWithProCaptcha;
Update your App.jsx:
// src/App.jsx
import React from 'react';
import ContactFormWithProCaptcha from './ContactFormWithProCaptcha';
import './App.css';
function App() {
return (
<div className="App">
<ContactFormWithProCaptcha />
</div>
);
}
export default App;
This example demonstrates:
- Contact form with ProCaptcha protection
- Form validation
- CAPTCHA verification
- Error handling
- User feedback
- Privacy-preserving bot protection
Common Issues / Troubleshooting
Provider not configured: Make sure you've wrapped your app with
ProsopoProviderand provided the required configuration (dappName, networkId).Hook not working: Ensure you're using
useProsopohook inside a component that's wrapped byProsopoProvider. The hook must be used within the provider context.Verification failing: Check that the CAPTCHA challenge is completed correctly. The verification process is async, so handle it with proper error handling.
Network configuration: Ensure the
networkIdin the provider config matches your environment. Use 'development' for local development.Captcha not rendering: Make sure
captchais available from the hook before callingcaptcha.render(). Check that the provider is properly initialized.State management: Track verification state in your component. The
verify()function returns a promise that resolves to a boolean indicating verification success.
Next Steps
Now that you have an understanding of ProCaptcha:
- Learn about advanced CAPTCHA configurations
- Explore network-specific settings
- Implement CAPTCHA in different form types
- Add CAPTCHA to authentication flows
- Learn about privacy-preserving features
- Check the official repository: https://github.com/prosopo/captcha
- Look for part 57 of this series for more advanced topics
Summary
You've successfully integrated ProCaptcha into your React application and created CAPTCHA protection for forms. ProCaptcha provides a privacy-preserving, decentralized solution for bot protection with flexible configuration options.
SEO Keywords
procaptcha
React ProCaptcha
procaptcha tutorial
React privacy CAPTCHA
procaptcha installation
React Prosopo CAPTCHA
procaptcha example
React decentralized CAPTCHA
procaptcha setup
React bot protection
procaptcha customization
React CAPTCHA library
procaptcha verification
React privacy-preserving
procaptcha getting started



Top comments (0)