Build a fully functional contact form in React with hooks, validation, and email notifications. No backend server or API routes needed.
Create a free FormsList account, add a new form, and copy the endpoint URL. This endpoint accepts POST requests with form data and handles storage, validation, and email delivery on the server side.
Create a new component that uses useState to track field values and submission state. The form collects name, email, and message fields. Using controlled inputs gives you full control over validation and UX before the data is sent.
import { useState, ChangeEvent, FormEvent } from "react";
interface FormFields {
name: string;
email: string;
message: string;
}
export default function ContactForm() {
const [fields, setFields] = useState<FormFields>({ name: "", email: "", message: "" });
const [status, setStatus] = useState<"idle" | "sending" | "sent" | "error">("idle");
function handleChange(e: ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) {
setFields({ ...fields, [e.target.name]: e.target.value });
}
async function handleSubmit(e: FormEvent) {
e.preventDefault();
setStatus("sending");
try {
const res = await fetch("https://formslist.com/f/YOUR_FORM_HASH", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(fields),
});
if (res.ok) {
setStatus("sent");
setFields({ name: "", email: "", message: "" });
} else {
setStatus("error");
}
} catch {
setStatus("error");
}
}
return (
<form onSubmit={handleSubmit} style={{ maxWidth: 480 }}>
<div>
<label htmlFor="name">Name</label>
<input id="name" name="name" value={fields.name} onChange={handleChange} required />
</div>
<div>
<label htmlFor="email">Email</label>
<input id="email" name="email" type="email" value={fields.email} onChange={handleChange} required />
</div>
<div>
<label htmlFor="message">Message</label>
<textarea id="message" name="message" rows={4} value={fields.message} onChange={handleChange} required />
</div>
<button type="submit" disabled={status === "sending"}>
{status === "sending" ? "Sending..." : "Send Message"}
</button>
{status === "sent" && <p style={{ color: "green" }}>Thank you! Your message has been sent.</p>}
{status === "error" && <p style={{ color: "red" }}>Something went wrong. Please try again.</p>}
</form>
);
}Enhance the form with inline validation before submission. Check that the email format is valid and the message meets a minimum length. This improves user experience by catching mistakes early, while FormsList still validates on the server side as a safety net.
function validate(fields: FormFields): Record<string, string> {
const errors: Record<string, string> = {};
if (fields.name.trim().length < 2) errors.name = "Name must be at least 2 characters.";
if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(fields.email)) errors.email = "Enter a valid email address.";
if (fields.message.trim().length < 10) errors.message = "Message must be at least 10 characters.";
return errors;
}
// Inside handleSubmit, before sending:
const errors = validate(fields);
if (Object.keys(errors).length > 0) {
// Display errors to the user
setFieldErrors(errors);
setStatus("idle");
return;
}Import and render ContactForm in your app. Submit a test entry to verify everything works end to end. In the FormsList dashboard, enable email forwarding and optionally connect Slack or webhook integrations to receive real-time alerts when someone contacts you.
import ContactForm from "./ContactForm";
function App() {
return (
<div className="App">
<h1>Get in Touch</h1>
<ContactForm />
</div>
);
}
export default App;Learn how to add a working contact form to your Next.js application in minutes. No backend code required — just create your form, point it at FormsList, and start receiving submissions by email.
Learn moreLearn the best practices for validating form data on both the client and server side. Improve user experience, reduce errors, and keep your data clean.
Learn moreLearn how to process form submissions on any website without writing server-side code. Use a form backend service to receive, store, and forward submissions by email.
Learn moreSet up your form backend in under a minute. No server required, no complex configuration — just a simple endpoint for your forms.