如何在 Next.js 中构建联系表单(无需后端)
Source: Dev.to

选项速览
当你搜索 “Next.js contact form” 时,通常会看到三种方法:
- Route Handler + email API (Resend, SendGrid, etc.) – 完全控制,但所有东西都由你自己负责。
- Server Actions – 更清晰的开发体验,但你仍需自行处理邮件 + 垃圾邮件。
- Form‑backend service – 将数据发送到托管的端点;它会处理存储 + 邮件。
对于大多数项目——着陆页、SaaS 网站、静态应用——选项 3 是最快的路径。本指南将详细介绍这种方法。
最简版本:仅 HTML
如果你不需要加载状态或交互功能,可以使用纯 HTML 表单,完全不使用 JavaScript:
<form action="https://formtorch.com/f/YOUR_FORM_ID" method="POST">
<input type="text" name="name" placeholder="Name" required />
<input type="email" name="email" placeholder="Email" required />
<textarea name="message" placeholder="Message" required></textarea>
<button type="submit">Send message</button>
</form>这已经是一个可用的表单。浏览器会处理提交,提交后会自动重定向。
重定向到你自己的页面
<form action="/thank-you" method="POST">
<!-- fields -->
</form>对于许多使用场景来说,这已经足够。但 React 应用通常希望实现更具交互性的功能。
合适的 React 联系表单
以下是一个具备加载、成功和错误状态的可直接投入生产的组件:
"use client";
import { useState } from "react";
type FormState = "idle" | "loading" | "success" | "error";
export function ContactForm() {
const [state, setState] = useState<FormState>("idle");
async function handleSubmit(e: React.FormEvent) {
e.preventDefault();
setState("loading");
try {
const data = new FormData(e.currentTarget);
const res = await fetch("https://formtorch.com/f/YOUR_FORM_ID", {
method: "POST",
headers: { "X-Requested-With": "XMLHttpRequest" },
body: data,
});
if (!res.ok) throw new Error();
setState("success");
} catch {
setState("error");
}
}
if (state === "success") {
return (
<div>
<h3>Message sent.</h3>
<p>Thanks for reaching out. I’ll get back to you soon.</p>
</div>
);
}
return (
<form onSubmit={handleSubmit} noValidate>
<label>
Name
<input type="text" name="name" required />
</label>
<label>
Email
<input type="email" name="email" required />
</label>
<label>
Message
<textarea name="message" required></textarea>
</label>
{state === "error" && <p>Something went wrong. Try again.</p>}
<button type="submit" disabled={state === "loading"}>
{state === "loading" ? "Sending…" : "Send message"}
</button>
</form>
);
}为什么这样做效果好
- 使用
FormData而非 JSON: 自动捕获所有字段(包括文件)。 X-Requested-With请求头: 确保返回的是 JSON 而不是 HTML 重定向。- 加载中 + 按钮禁用: 防止重复提交。
添加验证
您可以在不更改结构的情况下添加客户端验证:
function validate(data: FormData) {
const errors: Record<string, string> = {};
if (!data.get("name")) errors.name = "Name is required";
const email = String(data.get("email") ?? "");
if (!email) errors.email = "Email is required";
else if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email))
errors.email = "Invalid email";
if (!data.get("message")) errors.message = "Message is required";
return errors;
}在 <form> 元素上添加 noValidate 以禁用浏览器的原生 UI,并自行渲染错误。
使用 react-hook-form
对于更复杂的表单,react-hook-form 简化了状态管理:
import { useForm } from "react-hook-form";
export function ContactForm() {
const { register, handleSubmit, formState } = useForm();
async function onSubmit(data: any) {
const formData = new FormData();
Object.entries(data).forEach(([k, v]) => formData.append(k, String(v)));
await fetch("https://formtorch.com/f/YOUR_FORM_ID", {
method: "POST",
headers: { "X-Requested-With": "XMLHttpRequest" },
body: formData,
});
}
return (
<form onSubmit={handleSubmit(onSubmit)} noValidate>
<input {...register("name")} placeholder="Name" required />
<input {...register("email")} type="email" placeholder="Email" required />
<textarea {...register("message")} placeholder="Message" required></textarea>
<button type="submit" disabled={formState.isSubmitting}>
{formState.isSubmitting ? "Sending…" : "Send"}
</button>
</form>
);
}使用像 Formtorch 这样的托管表单后端,你可以在几分钟内搭建出功能完整的联系表单——无需自定义 API、无需服务器端邮件处理,也不需要维护额外的基础设施。祝编码愉快!
关于垃圾信息怎么办?
每个公开的表单都会收到垃圾信息。
常见做法
- CAPTCHA(用户体验差)
- 蜜罐
- 限流
- 垃圾信息检测
一个简单的蜜罐字段:
<input type="text" name="website" style="display:none" tabindex="-1" autocomplete="off" />机器人会填写它。人类不会。
要点
你不需要专门搭建后端来发送联系表单的邮件。
实际设置
- 简单页面 → HTML 表单
- React 应用 →
fetch + FormData - 复杂表单 →
react-hook-form
其他的都只是管道工作。
可选:完全跳过后端
如果你不想处理电子邮件 API、垃圾邮件过滤或存储提交数据,可以使用托管的表单后端。
例如,Formtorch 为你提供:
- 可直接使用的端点
- 电子邮件通知
- 内置垃圾邮件过滤
这样你的表单就可以在没有任何服务器代码的情况下工作。
如果你想尝试:
无论哪种方式,一旦你了解了整个流程,联系表单就不再是“设置的麻烦”,而是一个已经解决的问题。
