I'm new to Next.js/React, and I am trying to build out a simple contact form and protect it with Cloudflare Turnstile. Without Turnstile, I have it working with no issue. The form submits to the API and sends the email out.
Enviroment:
Next.js 13 using 'use client'
importing:
import { useState } from 'react';
import { Turnstile } from '@marsidev/react-turnstile';
However, now that I am trying to check the form submission with Turnstile, I can never get the input field containing the response token. I can inspect the form and see the input field with the response token filled out. Here is my code:
Form
<form onSubmit={handleSubmit}>
<div className="flex flex-col gap-4">
<div className="form-control w-full max-w-md">
<label className="label" htmlFor="name">
<span className="label-text">Name</span>
</label>
<input
type="text"
id="name"
placeholder="Your Name"
required
className="input w-full max-w-md"
name="name"
onChange={(e) => {
setName(e.target.value);
}}
/>
</div>
<div className="form-control w-full max-w-md">
<label className="label" htmlFor="email">
<span className="label-text">Email</span>
</label>
<input
type="text"
id="email"
placeholder="Your Email"
required
className="input w-full max-w-md"
name="email"
onChange={(e) => {
setEmail(e.target.value);
}}
/>
</div>
<div className="form-control w-full max-w-md">
<label className="label" htmlFor="subject">
<span className="label-text">Subject</span>
</label>
<input
type="text"
id="subject"
placeholder="Subject"
required
className="input w-full max-w-md"
name="subject"
onChange={(e) => {
setSubject(e.target.value);
}}
/>
</div>
<div className="form-control">
<label className="label" htmlFor="message">
<span className="label-text">Message</span>
</label>
<textarea
className="textarea-bordered textarea h-48 w-full max-w-md"
id="message"
placeholder="Your message"
required
name="message"
onChange={(e) => {
setMessage(e.target.value);
}}
></textarea>
</div>
<Turnstile siteKey={cfSiteKey} />
<button type="submit" className="btn-primary btn w-full max-w-md">
Submit
</button>
</div>
</form>
Handler
let cfSiteKey: string;
const [name, setName] = useState('');
const [email, setEmail] = useState('');
const [subject, setSubject] = useState('');
const [message, setMessage] = useState('');
const [submitted, setSubmitted] = useState(false);
if (process.env.NEXT_PUBLIC_CLOUDFLARE_TURNSTILE_SITE_KEY) {
cfSiteKey= process.env.NEXT_PUBLIC_CLOUDFLARE_TURNSTILE_SITE_KEY;
} else {
throw new Error('Cloudflare Turnstile Secret Key Not Set');
}
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault();
const formData = new FormData();
const token = formData.get('cf-turnstile-response');
console.log(token);
const data = {
name,
email,
subject,
message,
token,
};
fetch('/api/contact', {
method: 'POST',
headers: {
Accept: 'application/json, text/plain, */*',
'Content-Type': 'application/json',
},
body: JSON.stringify(data),
}).then((res) => {
console.log('Response received');
if (res.status === 200) {
console.log('Response succeeded!');
setSubmitted(true);
setName('');
setEmail('');
setSubject('');
setMessage('');
}
});
};
Above, token always returns null when I submit the form. What am I missing or doing wrong here?
CodePudding user response:
I guess I will answer my question.
I had tried many things inside new FormData()
but they did not work. So I left it blank and looked elsewhere in my code for the problem.
The answer, along with correct typing is the following inside the handler:
const handleSubmit = (e: FormEvent<HTMLFormElement>) => {
e.preventDefault();
const formData = new FormData(e.currentTarget);
const token = formData.get('cf-turnstile-response') as string;
console.log(token);
Now the token is available and sent correctly to the API.