Skip to content

React Forms

Formkove works with any React setup. Here’s a simple approach using useState and fetch.

import { useState } from "react";
export default function ContactForm() {
const [status, setStatus] = useState("");
const [result, setResult] = useState("");
const handleSubmit = async (e) => {
e.preventDefault();
setStatus("Sending...");
const formData = new FormData(e.target);
const data = Object.fromEntries(formData.entries());
try {
const res = await fetch("https://app.formkove.com/api/forms/YOUR_FORM_ID/submissions", {
method: "POST",
headers: {
"Content-Type": "application/json",
"Accept": "application/json"
},
body: JSON.stringify(data)
});
const json = await res.json();
if (res.ok) {
setStatus("Message sent!");
setResult("");
e.target.reset();
} else {
setStatus("Error");
setResult(json.error || "Something went wrong.");
}
} catch (err) {
setStatus("Error");
setResult("Network error. Please try again.");
}
};
return (
<form onSubmit={handleSubmit}>
<input type="text" name="name" placeholder="Your name" required />
<input type="email" name="email" placeholder="Email address" required />
<textarea name="message" placeholder="Your message" required />
<button type="submit" disabled={status === "Sending..."}>
{status || "Send"}
</button>
{result && <p>{result}</p>}
</form>
);
}

If you prefer react-hook-form for validation and state management:

import { useState } from "react";
import { useForm } from "react-hook-form";
export default function ContactForm() {
const {
register,
handleSubmit,
reset,
formState: { errors, isSubmitting, isSubmitSuccessful }
} = useForm({ mode: "onTouched" });
const [message, setMessage] = useState("");
const onSubmit = async (data) => {
setMessage("Sending...");
try {
const res = await fetch("https://app.formkove.com/api/forms/YOUR_FORM_ID/submissions", {
method: "POST",
headers: {
"Content-Type": "application/json",
"Accept": "application/json"
},
body: JSON.stringify(data)
});
const json = await res.json();
if (res.ok) {
setMessage("Sent! We'll be in touch.");
reset();
} else {
setMessage(json.error || "Something went wrong.");
}
} catch (err) {
setMessage("Network error. Check your connection.");
}
};
return (
<form onSubmit={handleSubmit(onSubmit)}>
<div>
<input
type="text"
placeholder="Full name"
{...register("name", { required: "Name is required" })}
/>
{errors.name && <span>{errors.name.message}</span>}
</div>
<div>
<input
type="email"
placeholder="Email address"
{...register("email", {
required: "Email is required",
pattern: { value: /^\S+@\S+$/, message: "Invalid email" }
})}
/>
{errors.email && <span>{errors.email.message}</span>}
</div>
<div>
<textarea
placeholder="Message"
{...register("message", { required: "Message is required" })}
/>
{errors.message && <span>{errors.message.message}</span>}
</div>
<button type="submit" disabled={isSubmitting}>
{isSubmitting ? "Sending..." : "Send Message"}
</button>
{message && <p>{message}</p>}
</form>
);
}
  • Form ID goes in the URL, not as a hidden field in React
  • Use Content-Type: application/json for fetch submissions
  • Handle both success (200) and error responses
  • form.reset() clears the form after success