@thind9xdev/react-turnstile 1.0.0 → 1.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/README.md +433 -118
  2. package/package.json +2 -2
package/README.md CHANGED
@@ -1,27 +1,29 @@
1
1
  # React Cloudflare Turnstile
2
2
 
3
- A clean, modern React hook for Cloudflare Turnstile integration.
3
+ A modern and clean React library for integrating Cloudflare Turnstile.
4
4
 
5
- ## Install:
5
+ ## 📦 Installation
6
6
 
7
7
  ```bash
8
- npm i @thind9xdev/react-turnstile
8
+ npm install @thind9xdev/react-turnstile
9
9
  ```
10
10
 
11
- ## Import to React:
11
+ ## 🚀 Import into React
12
12
 
13
13
  ```tsx
14
- import { useTurnstile } from "@thind9xdev/react-turnstile";
14
+ import { useTurnstile, TurnstileComponent } from "@thind9xdev/react-turnstile";
15
15
  ```
16
16
 
17
- ## Basic Usage
17
+ ## 📝 How to Use the Hook (useTurnstile)
18
+
19
+ ### Basic Usage with Hook
18
20
 
19
21
  ```tsx
20
22
  import React from "react";
21
23
  import { useTurnstile } from "@thind9xdev/react-turnstile";
22
24
 
23
- const YourComponent = () => {
24
- const siteKey = "YOUR_SITE_KEY";
25
+ const MyComponent = () => {
26
+ const siteKey = "YOUR_SITE_KEY"; // Replace with your actual site key
25
27
  const { ref, token, error, isLoading } = useTurnstile(siteKey);
26
28
 
27
29
  if (isLoading) {
@@ -32,7 +34,7 @@ const YourComponent = () => {
32
34
  return <div>Error: {error}</div>;
33
35
  }
34
36
 
35
- // You can use token to send request to API
37
+ // You can use the token to send requests to your API
36
38
  return (
37
39
  <div>
38
40
  <div ref={ref}></div>
@@ -41,16 +43,16 @@ const YourComponent = () => {
41
43
  );
42
44
  };
43
45
 
44
- export default YourComponent;
46
+ export default MyComponent;
45
47
  ```
46
48
 
47
- ## Advanced Usage
49
+ ### Advanced Usage with Hook
48
50
 
49
51
  ```tsx
50
52
  import React from "react";
51
53
  import { useTurnstile, TurnstileOptions } from "@thind9xdev/react-turnstile";
52
54
 
53
- const YourComponent = () => {
55
+ const AdvancedComponent = () => {
54
56
  const siteKey = "YOUR_SITE_KEY";
55
57
  const options: TurnstileOptions = {
56
58
  theme: "light",
@@ -75,74 +77,231 @@ const YourComponent = () => {
75
77
  try {
76
78
  const currentToken = getResponse();
77
79
  if (currentToken) {
78
- // Send request to your API with the token
80
+ // Send request to API with token
79
81
  console.log("Current token:", currentToken);
82
+
83
+ // Example API call
84
+ const response = await fetch('/api/verify', {
85
+ method: 'POST',
86
+ headers: { 'Content-Type': 'application/json' },
87
+ body: JSON.stringify({ token: currentToken })
88
+ });
89
+
90
+ const result = await response.json();
91
+ console.log("Verification result:", result);
80
92
  } else {
81
- // Execute Turnstile if no token available
93
+ // Execute Turnstile if no token yet
82
94
  execute();
83
95
  }
84
96
  } catch (err) {
85
- console.error("Failed to get Turnstile token:", err);
97
+ console.error("Unable to get Turnstile token:", err);
86
98
  }
87
99
  };
88
100
 
89
101
  const handleReset = () => {
90
- reset();
102
+ reset(); // Reset widget to initial state
91
103
  };
92
104
 
93
105
  return (
94
106
  <div>
95
107
  <div ref={ref}></div>
96
108
  <button onClick={handleSubmit} disabled={isLoading}>
97
- {isLoading ? "Loading..." : "Submit"}
109
+ {isLoading ? "Verifying..." : "Submit"}
98
110
  </button>
99
111
  <button onClick={handleReset} disabled={isLoading}>
100
112
  Reset Turnstile
101
113
  </button>
102
114
  {error && <p style={{ color: "red" }}>Error: {error}</p>}
103
- {token && <p style={{ color: "green" }}>Token ready!</p>}
115
+ {token && <p style={{ color: "green" }}>Token is ready!</p>}
104
116
  </div>
105
117
  );
106
118
  };
107
119
 
108
- export default YourComponent;
120
+ export default AdvancedComponent;
109
121
  ```
110
122
 
111
- ## Invisible/Execute Mode Usage
123
+ ## 🧩 How to Use the Component (TurnstileComponent)
124
+
125
+ ### Basic Usage with Component
112
126
 
113
127
  ```tsx
114
- import React from "react";
115
- import { useTurnstile, TurnstileOptions } from "@thind9xdev/react-turnstile";
128
+ import React, { useRef } from "react";
129
+ import { TurnstileComponent, TurnstileComponentRef } from "@thind9xdev/react-turnstile";
116
130
 
117
- const InvisibleTurnstile = () => {
131
+ const ComponentExample = () => {
132
+ const turnstileRef = useRef<TurnstileComponentRef>(null);
118
133
  const siteKey = "YOUR_SITE_KEY";
119
- const options: TurnstileOptions = {
120
- appearance: "execute", // Invisible mode
121
- execution: "execute"
134
+
135
+ const handleSubmit = () => {
136
+ const token = turnstileRef.current?.getResponse();
137
+ if (token) {
138
+ console.log("Token from component:", token);
139
+ // Send token to your API
140
+ } else {
141
+ console.log("No token yet, executing verification...");
142
+ turnstileRef.current?.execute();
143
+ }
122
144
  };
145
+
146
+ const handleReset = () => {
147
+ turnstileRef.current?.reset();
148
+ };
149
+
150
+ return (
151
+ <div>
152
+ <h3>Using TurnstileComponent</h3>
153
+
154
+ <TurnstileComponent
155
+ ref={turnstileRef}
156
+ siteKey={siteKey}
157
+ theme="auto"
158
+ size="normal"
159
+ className="my-turnstile"
160
+ style={{ margin: "20px 0" }}
161
+ />
162
+
163
+ <div>
164
+ <button onClick={handleSubmit}>
165
+ Submit Form
166
+ </button>
167
+ <button onClick={handleReset}>
168
+ Reset
169
+ </button>
170
+ </div>
171
+ </div>
172
+ );
173
+ };
174
+
175
+ export default ComponentExample;
176
+ ```
177
+
178
+ ### Component Usage with Advanced Options
179
+
180
+ ```tsx
181
+ import React, { useRef, useState } from "react";
182
+ import { TurnstileComponent, TurnstileComponentRef } from "@thind9xdev/react-turnstile";
183
+
184
+ const AdvancedComponentExample = () => {
185
+ const turnstileRef = useRef<TurnstileComponentRef>(null);
186
+ const [status, setStatus] = useState<string>("");
187
+ const siteKey = "YOUR_SITE_KEY";
188
+
189
+ const handleSuccess = (token: string) => {
190
+ setStatus(`Verification successful! Token: ${token.substring(0, 20)}...`);
191
+ };
192
+
193
+ const handleError = (error?: string) => {
194
+ setStatus(`Verification error: ${error || "Unknown"}`);
195
+ };
196
+
197
+ const handleLoad = () => {
198
+ setStatus("Turnstile loaded");
199
+ };
200
+
201
+ return (
202
+ <div>
203
+ <h3>Component with callback handlers</h3>
204
+
205
+ <TurnstileComponent
206
+ ref={turnstileRef}
207
+ siteKey={siteKey}
208
+ theme="dark"
209
+ size="compact"
210
+ language="en"
211
+ onSuccess={handleSuccess}
212
+ onError={handleError}
213
+ onLoad={handleLoad}
214
+ className="custom-turnstile"
215
+ style={{
216
+ border: "1px solid #ddd",
217
+ borderRadius: "8px",
218
+ padding: "10px"
219
+ }}
220
+ />
221
+
222
+ {status && (
223
+ <div style={{
224
+ marginTop: "10px",
225
+ padding: "10px",
226
+ backgroundColor: "#f5f5f5",
227
+ borderRadius: "4px"
228
+ }}>
229
+ {status}
230
+ </div>
231
+ )}
232
+ </div>
233
+ );
234
+ };
235
+
236
+ export default AdvancedComponentExample;
237
+ ```
238
+
239
+ ## 🔍 Invisible Mode
240
+
241
+ ### Using Invisible Mode with Hook
242
+
243
+ ```tsx
244
+ import React, { useState } from "react";
245
+ import { useTurnstile } from "@thind9xdev/react-turnstile";
246
+
247
+ const InvisibleTurnstile = () => {
248
+ const [email, setEmail] = useState("");
249
+ const siteKey = "YOUR_SITE_KEY";
123
250
 
124
- const { ref, token, error, isLoading, execute } = useTurnstile(siteKey, options);
251
+ const { ref, token, error, isLoading, execute } = useTurnstile(siteKey, {
252
+ appearance: "execute", // Invisible mode
253
+ execution: "execute",
254
+ theme: "light"
255
+ });
125
256
 
126
- const handleFormSubmit = async (e: React.FormEvent) => {
257
+ const handleSubmit = async (e: React.FormEvent) => {
127
258
  e.preventDefault();
128
-
259
+
129
260
  if (!token) {
130
261
  // Execute Turnstile verification
262
+ console.log("Verifying...");
131
263
  execute();
132
264
  return;
133
265
  }
134
266
 
135
- // Proceed with form submission using the token
136
- console.log("Submitting with token:", token);
267
+ // Submit form with token
268
+ try {
269
+ const response = await fetch("/api/submit", {
270
+ method: "POST",
271
+ headers: { "Content-Type": "application/json" },
272
+ body: JSON.stringify({ email, token })
273
+ });
274
+
275
+ if (response.ok) {
276
+ console.log("Form submitted successfully!");
277
+ setEmail("");
278
+ }
279
+ } catch (err) {
280
+ console.error("Form submission error:", err);
281
+ }
137
282
  };
138
283
 
139
284
  return (
140
- <form onSubmit={handleFormSubmit}>
141
- <div ref={ref}></div>
142
- <input type="email" placeholder="Your email" required />
143
- <button type="submit" disabled={isLoading}>
144
- {isLoading ? "Verifying..." : "Submit"}
285
+ <form onSubmit={handleSubmit}>
286
+ {/* Hidden container for Turnstile */}
287
+ <div ref={ref} style={{ display: "none" }}></div>
288
+
289
+ <div>
290
+ <label htmlFor="email">Email:</label>
291
+ <input
292
+ type="email"
293
+ id="email"
294
+ value={email}
295
+ onChange={(e) => setEmail(e.target.value)}
296
+ required
297
+ disabled={isLoading}
298
+ />
299
+ </div>
300
+
301
+ <button type="submit" disabled={isLoading || !email}>
302
+ {isLoading ? "Verifying..." : "Register"}
145
303
  </button>
304
+
146
305
  {error && <p style={{ color: "red" }}>{error}</p>}
147
306
  </form>
148
307
  );
@@ -151,7 +310,7 @@ const InvisibleTurnstile = () => {
151
310
  export default InvisibleTurnstile;
152
311
  ```
153
312
 
154
- ## API Reference
313
+ ## 📚 API Documentation
155
314
 
156
315
  ### `useTurnstile(siteKey, options?)`
157
316
 
@@ -160,66 +319,101 @@ export default InvisibleTurnstile;
160
319
  - `options` (TurnstileOptions, optional): Configuration options
161
320
 
162
321
  #### Options (TurnstileOptions):
163
- - `theme` ('light' | 'dark' | 'auto', optional): Widget theme (default: 'auto')
164
- - `size` ('normal' | 'compact', optional): Widget size (default: 'normal')
165
- - `language` (string, optional): Language code (default: 'auto')
166
- - `retry` ('auto' | 'never', optional): Retry behavior (default: 'auto')
167
- - `retry-interval` (number, optional): Retry interval in milliseconds
168
- - `refresh-expired` ('auto' | 'manual' | 'never', optional): Token refresh behavior (default: 'auto')
169
- - `appearance` ('always' | 'execute' | 'interaction-only', optional): When to show the widget (default: 'always')
170
- - `execution` ('render' | 'execute', optional): Execution mode (default: 'render')
322
+ - `theme` ('light' | 'dark' | 'auto'): Widget theme (default: 'auto')
323
+ - `size` ('normal' | 'compact'): Widget size (default: 'normal')
324
+ - `language` (string): Language code (default: 'auto')
325
+ - `retry` ('auto' | 'never'): Retry behavior (default: 'auto')
326
+ - `retry-interval` (number): Retry interval (milliseconds)
327
+ - `refresh-expired` ('auto' | 'manual' | 'never'): Token refresh behavior (default: 'auto')
328
+ - `appearance` ('always' | 'execute' | 'interaction-only'): When to show the widget (default: 'always')
329
+ - `execution` ('render' | 'execute'): Execution mode (default: 'render')
330
+ - `onLoad` (function): Callback when widget loads
331
+ - `onSuccess` (function): Callback on successful verification
332
+ - `onError` (function): Callback on error
333
+ - `onExpire` (function): Callback when token expires
334
+ - `onTimeout` (function): Callback on timeout
171
335
 
172
336
  #### Returns:
173
- - `ref` (React.RefObject): Ref to attach to the container div element
174
- - `token` (string | null): The Turnstile token
175
- - `error` (string | null): Error message if something went wrong
337
+ - `ref` (React.RefObject): Ref to attach to the container div
338
+ - `token` (string | null): Turnstile token
339
+ - `error` (string | null): Error message if any
176
340
  - `isLoading` (boolean): Loading state
177
- - `reset` (function): Function to reset the widget
178
- - `execute` (function): Function to manually execute Turnstile (for invisible mode)
179
- - `getResponse` (function): Function to get the current token
180
- - `widgetId` (string | null): The widget ID returned by Turnstile
341
+ - `reset` (function): Reset the widget
342
+ - `execute` (function): Manually execute Turnstile (for invisible mode)
343
+ - `getResponse` (function): Get the current token
344
+ - `widgetId` (string | null): Widget ID returned by Turnstile
181
345
 
182
- ## TypeScript Support
346
+ ### `TurnstileComponent`
183
347
 
184
- This package includes full TypeScript support with exported interfaces:
348
+ #### Props:
349
+ - `siteKey` (string): Your Cloudflare Turnstile site key
350
+ - `className` (string, optional): CSS class for the container
351
+ - `style` (React.CSSProperties, optional): Inline styles for the container
352
+ - All options from `TurnstileOptions`
353
+
354
+ #### Ref Methods:
355
+ - `reset()`: Reset the widget to its initial state
356
+ - `execute()`: Manually execute verification
357
+ - `getResponse()`: Get the current token
358
+
359
+ ## 🎨 TypeScript Support
360
+
361
+ This library includes full TypeScript support with exported interfaces:
185
362
 
186
363
  ```tsx
187
- import { useTurnstile, TurnstileResponse, TurnstileOptions } from "@thind9xdev/react-turnstile";
364
+ import {
365
+ useTurnstile,
366
+ TurnstileComponent,
367
+ TurnstileResponse,
368
+ TurnstileOptions,
369
+ TurnstileComponentProps,
370
+ TurnstileComponentRef
371
+ } from "@thind9xdev/react-turnstile";
188
372
  ```
189
373
 
190
- ## Widget Themes and Appearance
374
+ ## 🎭 Appearance and Display Modes
191
375
 
192
376
  ### Themes
193
377
  - `light`: Light theme
194
- - `dark`: Dark theme
195
- - `auto`: Automatically matches user's system preference
378
+ - `dark`: Dark theme
379
+ - `auto`: Follows user's system settings
196
380
 
197
381
  ### Sizes
198
- - `normal`: Standard size widget
199
- - `compact`: Smaller, compact widget
382
+ - `normal`: Standard widget size
383
+ - `compact`: Compact widget size
200
384
 
201
385
  ### Appearance Modes
202
- - `always`: Widget is always visible (default)
203
- - `execute`: Invisible mode - widget only appears during execution
386
+ - `always`: Widget always visible (default)
387
+ - `execute`: Invisible mode - widget appears only when executed
204
388
  - `interaction-only`: Widget appears only when user interaction is required
205
389
 
206
- ## Features
390
+ ## Features
207
391
 
208
- - ✅ Clean and modern React hook
392
+ - ✅ Modern, clean React hook
209
393
  - ✅ Full TypeScript support
210
- - ✅ Automatic script loading and cleanup
394
+ - ✅ Auto script loading and cleanup
211
395
  - ✅ Error handling
212
- - ✅ Loading states
396
+ - ✅ Loading state
213
397
  - ✅ Manual token refresh and reset
214
- - ✅ Support for invisible mode
215
- - ✅ Theme and size customization
216
- - ✅ Language support
398
+ - ✅ Invisible mode support
399
+ - ✅ Customizable appearance and size
400
+ - ✅ Multi-language support
217
401
  - ✅ Comprehensive widget lifecycle management
218
- - ✅ Zero dependencies (peer dependency: React >=16.8.0)
402
+ - ✅ No dependencies (peer dependency: React >=16.8.0)
403
+
404
+ ## 🔧 Get Your Site Key
405
+
406
+ 1. Go to [Cloudflare Dashboard](https://dash.cloudflare.com/)
407
+ 2. Navigate to "Turnstile"
408
+ 3. Create a new site
409
+ 4. Copy your **Site Key** and **Secret Key**
219
410
 
220
- # Backend Integration
411
+ ### Test Site Key
412
+ For testing, you can use: `1x00000000000000000000AA`
221
413
 
222
- ## Verify Turnstile token from React with Node.js/Express Back-End:
414
+ ## 🔧 Backend Integration
415
+
416
+ ### Verify Turnstile token with Node.js/Express:
223
417
 
224
418
  ```javascript
225
419
  const express = require('express');
@@ -228,13 +422,16 @@ const app = express();
228
422
 
229
423
  app.use(express.json());
230
424
 
231
- const TURNSTILE_SECRET_KEY = 'YOUR_SECRET_KEY';
425
+ const TURNSTILE_SECRET_KEY = 'YOUR_SECRET_KEY'; // Replace with your actual secret key
232
426
 
233
427
  app.post('/verify-turnstile', async (req, res) => {
234
428
  const { token, remoteip } = req.body;
235
429
 
236
430
  if (!token) {
237
- return res.status(400).json({ success: false, message: 'Missing token' });
431
+ return res.status(400).json({
432
+ success: false,
433
+ message: 'Missing token'
434
+ });
238
435
  }
239
436
 
240
437
  try {
@@ -247,7 +444,10 @@ app.post('/verify-turnstile', async (req, res) => {
247
444
  const { success, error_codes } = response.data;
248
445
 
249
446
  if (success) {
250
- res.json({ success: true, message: 'Verification successful' });
447
+ res.json({
448
+ success: true,
449
+ message: 'Verification successful'
450
+ });
251
451
  } else {
252
452
  res.status(400).json({
253
453
  success: false,
@@ -257,33 +457,41 @@ app.post('/verify-turnstile', async (req, res) => {
257
457
  }
258
458
  } catch (error) {
259
459
  console.error('Turnstile verification error:', error);
260
- res.status(500).json({ success: false, message: 'Internal server error' });
460
+ res.status(500).json({
461
+ success: false,
462
+ message: 'Internal server error'
463
+ });
261
464
  }
262
465
  });
466
+
467
+ app.listen(3000, () => {
468
+ console.log('Server running on port 3000');
469
+ });
263
470
  ```
264
471
 
265
- ## Verify Turnstile token with NestJS Back-End:
472
+ ### Verify Turnstile token with NestJS:
266
473
 
267
- ### Create TurnstileMiddleware:
474
+ #### Create TurnstileGuard:
268
475
  ```bash
269
- nest generate middleware turnstile
476
+ nest generate guard turnstile
270
477
  ```
271
478
 
272
- ### Add middleware code:
479
+ #### Add code for the guard:
273
480
  ```typescript
274
- import { Injectable, NestMiddleware } from '@nestjs/common';
275
- import { Request, Response, NextFunction } from 'express';
481
+ import { Injectable, CanActivate, ExecutionContext, UnauthorizedException } from '@nestjs/common';
482
+ import { Request } from 'express';
276
483
  import axios from 'axios';
277
484
 
278
485
  @Injectable()
279
- export class TurnstileMiddleware implements NestMiddleware {
280
- private secretKey = 'YOUR_SECRET_KEY';
486
+ export class TurnstileGuard implements CanActivate {
487
+ private readonly secretKey = 'YOUR_SECRET_KEY'; // Replace with your actual secret key
281
488
 
282
- async use(req: Request, res: Response, next: NextFunction) {
283
- const turnstileToken = req.body.turnstileToken;
489
+ async canActivate(context: ExecutionContext): Promise<boolean> {
490
+ const request = context.switchToHttp().getRequest<Request>();
491
+ const turnstileToken = request.body.token;
284
492
 
285
493
  if (!turnstileToken) {
286
- return res.status(400).json({ message: 'Missing turnstileToken' });
494
+ throw new UnauthorizedException('Missing Turnstile token');
287
495
  }
288
496
 
289
497
  try {
@@ -292,78 +500,185 @@ export class TurnstileMiddleware implements NestMiddleware {
292
500
  {
293
501
  secret: this.secretKey,
294
502
  response: turnstileToken,
295
- remoteip: req.ip
503
+ remoteip: request.ip
296
504
  }
297
505
  );
298
506
 
299
507
  const { success, error_codes } = response.data;
300
508
 
301
509
  if (!success) {
302
- return res.status(401).json({
303
- message: 'Invalid turnstileToken',
510
+ throw new UnauthorizedException({
511
+ message: 'Invalid Turnstile token',
304
512
  error_codes
305
513
  });
306
514
  }
307
515
 
308
- next();
516
+ return true;
309
517
  } catch (error) {
310
518
  console.error('Turnstile verification error:', error);
311
- return res.status(500).json({ message: 'Internal Server Error' });
519
+ throw new UnauthorizedException('Turnstile verification failed');
312
520
  }
313
521
  }
314
522
  }
315
523
  ```
316
524
 
317
- ## Getting Started with Cloudflare Turnstile
525
+ #### Use the Guard in Controller:
526
+ ```typescript
527
+ import { Controller, Post, UseGuards, Body } from '@nestjs/common';
528
+ import { TurnstileGuard } from './turnstile.guard';
529
+
530
+ @Controller('api')
531
+ export class AppController {
532
+ @Post('submit')
533
+ @UseGuards(TurnstileGuard)
534
+ submitForm(@Body() body: any) {
535
+ // Handle form logic after Turnstile verification
536
+ return { message: 'Form submitted successfully!' };
537
+ }
538
+ }
539
+ ```
540
+
541
+ ### Verification with PHP (Laravel):
542
+
543
+ ```php
544
+ <?php
545
+
546
+ use Illuminate\Http\Request;
547
+ use Illuminate\Support\Facades\Http;
548
+
549
+ class TurnstileController extends Controller
550
+ {
551
+ public function verify(Request $request)
552
+ {
553
+ $token = $request->input('token');
554
+ $secretKey = env('TURNSTILE_SECRET_KEY'); // Add to .env
555
+
556
+ if (!$token) {
557
+ return response()->json([
558
+ 'success' => false,
559
+ 'message' => 'Missing token'
560
+ ], 400);
561
+ }
562
+
563
+ $response = Http::post('https://challenges.cloudflare.com/turnstile/v0/siteverify', [
564
+ 'secret' => $secretKey,
565
+ 'response' => $token,
566
+ 'remoteip' => $request->ip()
567
+ ]);
568
+
569
+ $result = $response->json();
570
+
571
+ if ($result['success']) {
572
+ return response()->json([
573
+ 'success' => true,
574
+ 'message' => 'Verification successful'
575
+ ]);
576
+ } else {
577
+ return response()->json([
578
+ 'success' => false,
579
+ 'message' => 'Verification failed',
580
+ 'error_codes' => $result['error_codes'] ?? []
581
+ ], 400);
582
+ }
583
+ }
584
+ }
585
+ ```
586
+
587
+ ## 🚀 Getting Started with Cloudflare Turnstile
318
588
 
319
- 1. **Sign up for Cloudflare**: Visit [Cloudflare Dashboard](https://dash.cloudflare.com/)
320
- 2. **Navigate to Turnstile**: Go to "Turnstile" in the sidebar
589
+ 1. **Sign up for Cloudflare**: Go to [Cloudflare Dashboard](https://dash.cloudflare.com/)
590
+ 2. **Navigate to Turnstile**: Find "Turnstile" in the sidebar
321
591
  3. **Create a Site**: Click "Add Site" and configure your domain
322
592
  4. **Get your keys**: Copy your Site Key and Secret Key
323
- 5. **Configure your site**: Set up allowed domains and other settings
593
+ 5. **Configure your site**: Set allowed domains and other settings
324
594
 
325
- ## Error Handling
595
+ ## ⚠️ Error Handling
326
596
 
327
597
  Common error codes and their meanings:
328
598
 
329
- - `missing-input-secret`: The secret parameter is missing
330
- - `invalid-input-secret`: The secret parameter is invalid or malformed
331
- - `missing-input-response`: The response parameter is missing
332
- - `invalid-input-response`: The response parameter is invalid or malformed
333
- - `bad-request`: The request is invalid or malformed
334
- - `timeout-or-duplicate`: The response parameter has already been validated before
599
+ - `missing-input-secret`: Missing secret parameter
600
+ - `invalid-input-secret`: Invalid or malformed secret parameter
601
+ - `missing-input-response`: Missing response parameter
602
+ - `invalid-input-response`: Invalid or malformed response parameter
603
+ - `bad-request`: Invalid or malformed request
604
+ - `timeout-or-duplicate`: Response parameter has already been validated
605
+
606
+ ### Error Handling Example:
607
+
608
+ ```tsx
609
+ import { useTurnstile } from "@thind9xdev/react-turnstile";
610
+
611
+ const ErrorHandlingExample = () => {
612
+ const { ref, token, error, isLoading, reset } = useTurnstile("YOUR_SITE_KEY");
613
+
614
+ const getErrorMessage = (error: string) => {
615
+ switch (error) {
616
+ case 'timeout-or-duplicate':
617
+ return 'Token has been used or timed out. Please try again.';
618
+ case 'invalid-input-response':
619
+ return 'Invalid response. Please refresh the page.';
620
+ default:
621
+ return `Verification error: ${error}`;
622
+ }
623
+ };
335
624
 
336
- ## Browser Support
625
+ return (
626
+ <div>
627
+ <div ref={ref}></div>
628
+ {error && (
629
+ <div style={{ color: 'red', marginTop: '10px' }}>
630
+ <p>{getErrorMessage(error)}</p>
631
+ <button onClick={reset}>Try Again</button>
632
+ </div>
633
+ )}
634
+ {token && <p style={{ color: 'green' }}>✅ Verification successful!</p>}
635
+ </div>
636
+ );
637
+ };
638
+ ```
337
639
 
338
- Cloudflare Turnstile works in all modern browsers that support:
640
+ ## 🌐 Browser Support
641
+
642
+ Cloudflare Turnstile works on all modern browsers supporting:
339
643
  - ES6 Promises
340
644
  - Fetch API or XMLHttpRequest
341
645
  - Modern JavaScript features
342
646
 
343
- ## Migration from reCAPTCHA
647
+ ## 🔄 Migration from reCAPTCHA
344
648
 
345
- If you're migrating from Google reCAPTCHA, the main differences are:
649
+ If you are migrating from Google reCAPTCHA, the main differences are:
346
650
 
347
- 1. **Script URL**: Uses Cloudflare's CDN instead of Google's
651
+ 1. **Script URL**: Use Cloudflare CDN instead of Google
348
652
  2. **API Methods**: Different method names and parameters
349
- 3. **Verification endpoint**: Uses Cloudflare's verification API
653
+ 3. **Verification endpoint**: Use Cloudflare's verification API
350
654
  4. **Configuration options**: Different theme and customization options
351
- 5. **Privacy**: Better privacy protection as Cloudflare doesn't track users
655
+ 5. **Privacy**: Better privacy as Cloudflare does not track users
656
+
657
+ ### Comparison Table:
658
+
659
+ | reCAPTCHA | Turnstile |
660
+ |-----------|-----------|
661
+ | `grecaptcha.render()` | `turnstile.render()` |
662
+ | `grecaptcha.reset()` | `turnstile.reset()` |
663
+ | `grecaptcha.getResponse()` | `turnstile.getResponse()` |
664
+ | Google CDN | Cloudflare CDN |
665
+ | Tracks users | Privacy-focused |
352
666
 
353
- ## Contributing
667
+ ## 🤝 Contributing
354
668
 
355
- Contributions are welcome! Please feel free to submit a Pull Request.
669
+ Contributions are welcome! Please open a Pull Request.
356
670
 
357
- ## License
671
+ ## 📄 License
358
672
 
359
673
  This project is licensed under the MIT License.
360
674
 
361
- ## Author
675
+ ## 👨‍💻 Author
362
676
 
363
- Copyright 2024 thind9xdev
677
+ Copyright 2025 thind9xdev
364
678
 
365
- ## Links
679
+ ## 🔗 Links
366
680
 
367
681
  - [Cloudflare Turnstile Documentation](https://developers.cloudflare.com/turnstile/)
368
- - [GitHub Repository](https://github.com/thind9xdev/react-turnstile1)
369
- - [NPM Package](https://www.npmjs.com/package/@thind9xdev/react-turnstile)
682
+ - [GitHub Repository](https://github.com/thind9xdev/react-turnstile)
683
+ - [NPM Package](https://www.npmjs.com/package/@thind9xdev/react-turnstile)
684
+ - [Quick Start Guide](./QUICK_START.md)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@thind9xdev/react-turnstile",
3
- "version": "1.0.0",
3
+ "version": "1.0.2",
4
4
  "description": "A React hook for Cloudflare Turnstile integration",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -8,7 +8,7 @@
8
8
  "build": "tsc",
9
9
  "clean": "rm -rf dist",
10
10
  "prebuild": "npm run clean",
11
- "prepare": "npm run build"
11
+ "prepublishOnly": "npm run build"
12
12
  },
13
13
  "keywords": [
14
14
  "react",