netlify-forms

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Netlify Forms

Netlify Forms

Netlify Forms collects HTML form submissions without server-side code. Form detection must be enabled in the Netlify UI (Forms section).
Netlify Forms 无需服务端代码即可收集HTML表单提交数据。你需要在Netlify UI的「表单」板块中启用表单检测功能。

Basic Setup

基础配置

Add
data-netlify="true"
and a unique
name
to the form:
html
<form name="contact" method="POST" data-netlify="true">
  <label>Name: <input type="text" name="name" /></label>
  <label>Email: <input type="email" name="email" /></label>
  <label>Message: <textarea name="message"></textarea></label>
  <button type="submit">Send</button>
</form>
Netlify's build system detects the form and injects a hidden
form-name
input automatically. For a custom success page, add
action="/thank-you"
to the form tag. Use paths without
.html
extension — Netlify serves
thank-you.html
at
/thank-you
by default, and the
.html
path returns 404.
给表单添加
data-netlify="true"
属性和唯一的
name
属性:
html
<form name="contact" method="POST" data-netlify="true">
  <label>Name: <input type="text" name="name" /></label>
  <label>Email: <input type="email" name="email" /></label>
  <label>Message: <textarea name="message"></textarea></label>
  <button type="submit">Send</button>
</form>
Netlify的构建系统会自动检测到表单,并注入一个隐藏的
form-name
输入框。如果需要自定义成功跳转页面,给表单标签添加
action="/thank-you"
属性即可。请使用不带
.html
后缀的路径——Netlify默认会在
/thank-you
路径下提供
thank-you.html
文件,带
.html
后缀的路径会返回404。

JavaScript-Rendered Forms (React, Vue, SSR Frameworks)

JavaScript渲染的表单(React、Vue、SSR框架)

For forms rendered by JavaScript frameworks (React, Vue, TanStack Start, Next.js, SvelteKit, Remix, Nuxt), Netlify's build parser cannot detect the form in static HTML. You MUST create a static HTML skeleton file for build-time form detection:
Create a static HTML file in
public/
(e.g.
public/__forms.html
) containing a hidden copy of each form:
html
<!DOCTYPE html>
<html>
  <body>
    <form name="contact" data-netlify="true" netlify-honeypot="bot-field" hidden>
      <input type="hidden" name="form-name" value="contact" />
      <input type="text" name="name" />
      <input type="email" name="email" />
      <textarea name="message"></textarea>
      <input name="bot-field" />
    </form>
  </body>
</html>
Rules:
  • The form
    name
    must exactly match the
    form-name
    value used in your component's fetch call
  • Include every field your component submits — Netlify validates field names against the registered form
  • Without this file, Netlify cannot detect the form and submissions will silently fail
Your component must also include a hidden
form-name
input:
jsx
<form name="contact" method="POST" data-netlify="true">
  <input type="hidden" name="form-name" value="contact" />
  {/* ... fields ... */}
</form>
对于由JavaScript框架(React、Vue、TanStack Start、Next.js、SvelteKit、Remix、Nuxt)渲染的表单,Netlify的构建解析器无法在静态HTML中检测到表单,你必须创建一个静态HTML骨架文件供构建阶段表单检测使用:
public/
目录下创建一个静态HTML文件(比如
public/__forms.html
),包含每个表单的隐藏副本:
html
<!DOCTYPE html>
<html>
  <body>
    <form name="contact" data-netlify="true" netlify-honeypot="bot-field" hidden>
      <input type="hidden" name="form-name" value="contact" />
      <input type="text" name="name" />
      <input type="email" name="email" />
      <textarea name="message"></textarea>
      <input name="bot-field" />
    </form>
  </body>
</html>
规则:
  • 表单的
    name
    属性值必须和组件fetch请求中使用的
    form-name
    值完全匹配
  • 包含组件提交的所有字段——Netlify会将字段名和已注册的表单做校验
  • 如果没有这个文件,Netlify无法检测到表单,提交会静默失败
你的组件还需要包含一个隐藏的
form-name
输入框:
jsx
<form name="contact" method="POST" data-netlify="true">
  <input type="hidden" name="form-name" value="contact" />
  {/* ... fields ... */}
</form>

AJAX Submissions

AJAX提交

Vanilla JavaScript

原生JavaScript

javascript
const form = document.querySelector("form");
form.addEventListener("submit", async (e) => {
  e.preventDefault();
  const formData = new FormData(form);
  await fetch("/", {
    method: "POST",
    headers: { "Content-Type": "application/x-www-form-urlencoded" },
    body: new URLSearchParams(formData).toString(),
  });
});
SSR frameworks (TanStack Start, Next.js, SvelteKit, Remix, Nuxt): The
fetch
URL must target the static skeleton file path (e.g.
"/__forms.html"
), not
"/"
. In SSR apps,
fetch("/")
is intercepted by the SSR catch-all function and never reaches Netlify's form processing middleware. See the React example and troubleshooting section below.
javascript
const form = document.querySelector("form");
form.addEventListener("submit", async (e) => {
  e.preventDefault();
  const formData = new FormData(form);
  await fetch("/", {
    method: "POST",
    headers: { "Content-Type": "application/x-www-form-urlencoded" },
    body: new URLSearchParams(formData).toString(),
  });
});
SSR框架(TanStack Start、Next.js、SvelteKit、Remix、Nuxt):
fetch
的请求地址必须指向静态骨架文件的路径(比如
"/__forms.html"
),不能
"/"
。在SSR应用中,
fetch("/")
会被SSR的全局捕获函数拦截,永远无法到达Netlify的表单处理中间件。参考下方的React示例和故障排查部分。

React Example

React示例

tsx
function ContactForm() {
  const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
    e.preventDefault();
    const formData = new FormData(e.currentTarget);
    // For SSR apps, use the skeleton file path instead of "/" (e.g. "/__forms.html")
    const response = await fetch("/__forms.html", {
      method: "POST",
      headers: { "Content-Type": "application/x-www-form-urlencoded" },
      body: new URLSearchParams(formData as any).toString(),
    });
    if (response.ok) {
      // Show success feedback
    }
  };

  return (
    <form name="contact" method="POST" data-netlify="true" onSubmit={handleSubmit}>
      <input type="hidden" name="form-name" value="contact" />
      <input type="text" name="name" placeholder="Name" />
      <input type="email" name="email" placeholder="Email" />
      <textarea name="message" placeholder="Message" />
      <button type="submit">Send</button>
    </form>
  );
}
SSR troubleshooting: If form submissions appear to succeed (200 response) but nothing shows in the Netlify Forms UI, the POST is likely being intercepted by the SSR function. Ensure
fetch
targets the skeleton file path (e.g.
"/__forms.html"
), not
"/"
. The skeleton file path routes through the CDN origin where Netlify's form handler runs.
tsx
function ContactForm() {
  const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
    e.preventDefault();
    const formData = new FormData(e.currentTarget);
    // For SSR apps, use the skeleton file path instead of "/" (e.g. "/__forms.html")
    const response = await fetch("/__forms.html", {
      method: "POST",
      headers: { "Content-Type": "application/x-www-form-urlencoded" },
      body: new URLSearchParams(formData as any).toString(),
    });
    if (response.ok) {
      // Show success feedback
    }
  };

  return (
    <form name="contact" method="POST" data-netlify="true" onSubmit={handleSubmit}>
      <input type="hidden" name="form-name" value="contact" />
      <input type="text" name="name" placeholder="Name" />
      <input type="email" name="email" placeholder="Email" />
      <textarea name="message" placeholder="Message" />
      <button type="submit">Send</button>
    </form>
  );
}
SSR故障排查: 如果表单提交看起来成功了(返回200响应),但Netlify Forms UI中没有显示任何数据,那么POST请求很可能被SSR函数拦截了。请确保
fetch
请求的目标是骨架文件路径(比如
"/__forms.html"
),而不是
"/"
。骨架文件路径会通过CDN源站路由,Netlify的表单处理程序会在那里运行。

Spam Filtering

垃圾信息过滤

Netlify uses Akismet automatically. Add a honeypot field for extra protection:
html
<form name="contact" method="POST" netlify-honeypot="bot-field" data-netlify="true">
  <p style="display:none">
    <label>Don't fill this out: <input name="bot-field" /></label>
  </p>
  <!-- visible fields -->
</form>
For reCAPTCHA, add
data-netlify-recaptcha="true"
to the form and include
<div data-netlify-recaptcha="true"></div>
where the widget should appear.
Netlify会自动使用Akismet进行垃圾信息过滤。你可以添加蜜罐字段来获得额外防护:
html
<form name="contact" method="POST" netlify-honeypot="bot-field" data-netlify="true">
  <p style="display:none">
    <label>Don't fill this out: <input name="bot-field" /></label>
  </p>
  <!-- visible fields -->
</form>
如果需要使用reCAPTCHA,给表单添加
data-netlify-recaptcha="true"
属性,然后在你想要展示验证码组件的位置加入
<div data-netlify-recaptcha="true"></div>
即可。

File Uploads

文件上传

html
<form name="upload" enctype="multipart/form-data" data-netlify="true">
  <input type="text" name="name" />
  <input type="file" name="attachment" />
  <button type="submit">Upload</button>
</form>
For AJAX file uploads, use
FormData
directly — do not set
Content-Type
(the browser sets it with the correct boundary):
javascript
await fetch("/", { method: "POST", body: new FormData(form) });
Limits: 8 MB max request size, 30-second timeout, one file per input field.
html
<form name="upload" enctype="multipart/form-data" data-netlify="true">
  <input type="text" name="name" />
  <input type="file" name="attachment" />
  <button type="submit">Upload</button>
</form>
对于AJAX文件上传,直接使用
FormData
即可——不要手动设置
Content-Type
(浏览器会自动设置正确的边界值):
javascript
await fetch("/", { method: "POST", body: new FormData(form) });
限制: 最大请求大小为8MB,超时时间30秒,每个输入字段仅支持上传一个文件。

Notifications

通知功能

Configure in the Netlify UI under Project configuration > Notifications:
  • Email: Auto-sends on submission. Add
    <input type="hidden" name="subject" value="Contact form" />
    for custom subject lines.
  • Slack: Via Netlify App for Slack.
  • Webhooks: Trigger external services on submission.
在Netlify UI的项目配置 > 通知路径下配置:
  • 邮件: 提交表单后自动发送邮件。添加
    <input type="hidden" name="subject" value="Contact form" />
    可以自定义邮件主题。
  • Slack: 通过Slack的Netlify应用实现。
  • Webhooks: 表单提交时触发外部服务。

Submissions API

提交数据API

Access submissions programmatically:
GET /api/v1/forms/{form_id}/submissions
Authorization: Bearer <PERSONAL_ACCESS_TOKEN>
Key endpoints:
ActionMethodPath
List formsGET
/api/v1/sites/{site_id}/forms
Get submissionsGET
/api/v1/forms/{form_id}/submissions
Get spamGET
/api/v1/forms/{form_id}/submissions?state=spam
Delete submissionDELETE
/api/v1/submissions/{id}
通过编程方式访问提交数据:
GET /api/v1/forms/{form_id}/submissions
Authorization: Bearer <PERSONAL_ACCESS_TOKEN>
核心接口:
操作请求方法路径
列出所有表单GET
/api/v1/sites/{site_id}/forms
获取提交数据GET
/api/v1/forms/{form_id}/submissions
获取垃圾提交数据GET
/api/v1/forms/{form_id}/submissions?state=spam
删除提交数据DELETE
/api/v1/submissions/{id}