オプション

Netlifyでホストされているサイトでのサーバーレスなフォーム処理 — デプロイ時にHTMLフォームを検出し、送信データを保存し、スパムをフィルタリングし、通知を送信します。Netlifyサイトに問い合わせフォーム、見込み客獲得フォーム、ファイルアップロードフォーム、またはニュースレター登録フォームを追加する際に利用してください。 AJAXによるフォーム送信の連携、カスタムのお礼ページの設定、フォームへのハニーポットやreCAPTCHAの追加、Next.js、Nuxt、SvelteKit、Astro、Gatsbyでのフォームの動作設定、Netlify APIを介したフォーム送信データの取得、送信データが欠落している場合のデバッグなど。

...すべて拡張します
22
更新された時間 2026年8月23日

netlify-formsについて

このスキルは、サーバーサイドのコードを記述することなくHTMLフォームの送信データを収集するためのNetlifyのサーバーレス機能「Netlify Forms」のガイドです。 これは、Netlify上でフォームを実際に登録し、データを収集する際に生じがちな課題を解決します。検出はデプロイ時にプリレンダリングされたHTMLを解析して行われるため、JavaScriptレンダリングやサーバーサイドレンダリングのアプリ内のフォームは、正しく設定されていない限り、気付かれないまま失敗してしまいます。このスキルでは、ライフサイクル全体と、注意すべき多くの落とし穴について解説します。

取り上げる機能には、`data-netlify`属性と一意のフォーム名を用いた基本設定、拡張子なしのアクションパスによるカスタムサンキューページ、およびJavaScriptフレームワーク(React、Vue、Astro、Next.js、 SvelteKit、Remix、Nuxt、TanStack Start)向けの重要なパターン:ビルド時の検出が成功するように、各フォームのフィールド名と一致する非表示のコピーを含む静的HTMLスケルトンファイル(例:public/__forms.html)を作成する方法です。 また、AJAX送信はx-www-form-urlencodedまたはmultipart/form-data(JSONは絶対に不可)でなければならないこと、SSRにおける「/」ではなくスケルトンパスを指定する際の注意点、自動Akismetによるスパムフィルタリングに加えハニーポットフィールドやreCAPTCHAの活用、 さらに、Netlifyの環境変数(SITE_RECAPTCHA_KEYおよびSITE_RECAPTCHA_SECRET)を通じて独自のreCAPTCHAキーを安全に利用する方法についても解説しています。これらはハードコードではなく、サーバー側で保持されます。また、ファイルアップロード、通知、および送信APIについても言及しています。

対象ユーザーは、Netlify上で静的サイトやフレームワークベースのサイトをデプロイし、お問い合わせ、フィードバック、リード獲得、ニュースレター、またはファイルアップロードフォームを必要とするフロントエンドおよびフルスタック開発者です。 代表的なユースケースとしては、AJAX 問い合わせフォームの接続、スパム対策の追加、SSR フレームワーク内でのフォームの検出、および送信は成功したように見えるものの Forms UI に表示されないケースのデバッグなどが挙げられます。

よくある質問

なぜフォームが検出されないのですか?

Netlify はデプロイ時にプリレンダリングされた HTML をスキャンします。JavaScript や SSR によってのみレンダリングされるフォームは、パーサーからは認識されません。各フォームの非表示のコピーと、それに対応するフィールド名を含む静的なスケルトン HTML ファイル(例:public/__forms.html)を追加してから、再デプロイする必要があります。

AJAXによる送信は成功しているのに、なぜフォームが表示されないのですか?

Netlify FormsはJSONを受け付けません。application/x-www-form-urlencoded(URLSearchParams経由)またはmultipart/form-dataで送信してください。 SSR アプリでは、fetch の対象を「/」ではなく、スケルトンファイルのパス(例: /__forms.html)に設定する必要があります。「/」を指定すると、SSR のキャッチオール機能によってインターセプトされてしまいます。

スパムフィルタリングはどのように機能しますか?

Akismetは自動的に動作します。ハニーポットフィールド(netlify-honeypot)やreCAPTCHAを追加することも可能です。スパムとしてフラグが立てられた送信データは、Forms UI内の別の「スパム」リストに自動的に移動されます。

reCAPTCHAの認証情報はどのように安全に扱われますか?

デフォルトでは、Netlify が reCAPTCHA をプロビジョニングします。独自のキーを使用するには、SITE_RECAPTCHA_KEY および SITE_RECAPTCHA_SECRET を Netlify 環境変数として設定してください。これにより、シークレットはサーバー側に保持され、クライアントコードにハードコードされることはありません。

フォーム検出機能を有効にすると、既存のデプロイに影響はありますか?

いいえ。検出機能は今後のデプロイにのみ影響するため、有効化した後は、すでに公開されているフォームが送信データの収集を開始する前に、新しいビルドをトリガーする必要があります。

GitHubで見る

Mark a form for detection with data-netlify="true" (or the bare netlify attribute — equivalent) on the <form> tag. Forms are detected by parsing the final built HTML at deploy time — there is no runtime API call or backend code. Client-side/JS-rendered/SSR forms are NOT in the built HTML and are never detected on their own; they require a static skeleton file (see below).

Prerequisite: form detection must be enabled once in the Netlify UI (Forms > Enable form detection). Takes effect on the next deploy.

Static HTML form

<form name="contact" method="POST" data-netlify="true">  <p><label>Your Name: <input type="text" name="name" /></label></p>  <p><label>Your Email: <input type="email" name="email" /></label></p>  <p><label>Message: <textarea name="message"></textarea></label></p>  <p><button type="submit">Send</button></p></form>
  • name sets the form name in the UI and must be unique per site.
  • At deploy, Netlify strips the data-netlify/netlify attribute and injects <input type="hidden" name="form-name" value="contact" />.
  • Add an <input name="email"> so the notification email's Reply-to is set to the submitter.

JS-rendered / SSR / framework forms (Next.js, Nuxt, SvelteKit, Astro, Gatsby)

Two required pieces:

1. Static skeleton file public/__forms.html — a hidden copy of each form with data-netlify="true", a hidden form-name input, and every field the component submits, with names matching exactly (Netlify validates field names against the registered form). Without this file, submissions silently fail.

<!-- public/__forms.html --><form name="pizzaOrder" data-netlify="true" hidden>  <input type="hidden" name="form-name" value="pizzaOrder" />  <input name="order" type="text" /></form>

2. The rendered form carries a matching hidden form-name input:

<form name="pizzaOrder" method="post" data-netlify="true" onSubmit={handleSubmit}>  <input type="hidden" name="form-name" value="pizzaOrder" />  <input name="order" type="text" onChange={handleChange} />  <input type="submit" /></form>

⚠️ SSR POST target: In SSR apps, fetch("/") is intercepted by the SSR catch-all function and never reaches form processing. POST to the static skeleton file itself — /__forms.html — not / or an arbitrary path.

⚠️ Astro on-demand routes: Routes with export const prerender = false or output: "server" are never scanned at build time, so their forms are never registered. Put the form on a prerendered page, or rely on the static skeleton file.

Next.js Runtime v5 (Next.js 13.5+): extract form definitions to the static skeleton file and submit via AJAX rather than full-page navigation. See https://docs.netlify.com/build/frameworks/framework-setup-guides/nextjs/overview#v5-breaking-changes

AJAX submission

const handleSubmit = event => {  event.preventDefault();  const formData = new FormData(event.target);  fetch("/__forms.html", {   // static sites may POST to "/"; SSR must target the skeleton file    method: "POST",    headers: { "Content-Type": "application/x-www-form-urlencoded" },    body: new URLSearchParams(formData).toString()  })    .then(() => alert("Thank you for your submission"))  // or navigate("/thank-you")    .catch(error => alert(error));};document.querySelector("form").addEventListener("submit", handleSubmit);
  • Body MUST be URL-encoded. JSON is NOT supported.
  • If the rendered form has no hidden form-name input, you MUST include a form-name field in the POST body.
  • The honeypot field name and g-recaptcha-response (if used) must be in the body — automatic with FormData().

File uploads

Add type="file"; optionally enctype="multipart/form-data" on the <form>. For AJAX file uploads, do NOT set a Content-Type header — let the browser set it (with the multipart boundary).

document.forms.fileForm.addEventListener("submit", event => {  event.preventDefault();  fetch("/", { body: new FormData(event.target), method: "POST" })  // no headers    .then(() => { /* success */ });});

Limits: one file per field (use multiple fields for multiple files) · 8 MB max request size · 30 s upload timeout · after form deletion, uploaded files stay at their direct URL for 24 h. PII uploads need extra security (Very Good Security integration).

Custom success page

Add an action path relative to site root, starting with /. Use extensionless paths — Netlify serves thank-you.html at /thank-you; the .html path returns 404.

<form name="contact" action="/thank-you" method="POST" data-netlify="true"></form>

Custom success alert is only possible via AJAX (substitute the redirect with your own logic).

Spam prevention

All submissions are filtered by Akismet. Passed → Verified submissions; flagged → Spam submissions. Honeypot/reCAPTCHA failures are rejected and appear in neither list.

Honeypot: add netlify-honeypot="bot-field" to the <form> and include a CSS-hidden field of that name. Any value entered → submission quietly rejected.

<form name="contact" method="POST" netlify-honeypot="bot-field" data-netlify="true">  <p class="hidden"><label>Don’t fill this out: <input name="bot-field" /></label></p>  <!-- real fields --></form>

Netlify reCAPTCHA 2: add data-netlify-recaptcha="true" to the <form> AND an empty <div data-netlify-recaptcha="true"></div> where it renders. Only ONE Netlify-provided challenge per page — for multiple, use custom reCAPTCHA. For JS-rendered forms, also add the div to the static skeleton file.

Custom reCAPTCHA 2: your own reCAPTCHA snippet + data-netlify-recaptcha="true" on the <form>, plus env vars:

  • SITE_RECAPTCHA_KEY — site key (scopes: Builds + Runtime)
  • SITE_RECAPTCHA_SECRET — secret (scope: Runtime)

Email notifications & subject line

Default sender: [email protected]. Set subject via a hidden subject input or the Netlify UI (Configuration > Notifications) — not both; the HTML value always overrides the UI.

<input type="hidden" name="subject" value="New lead from %{formName} (%{submissionId})" />

Variables: %{formName}, %{siteName}, %{submissionId}. Forms created before May 5, 2023 carry a [Netlify] subject prefix — remove it by adding the data-remove-prefix attribute to the subject input.

Set up notifications (email/webhook/Slack) in the UI: Configuration > Notifications > Form submission notifications > Add notification.

Reading submissions via the API

Use only documented surfaces. Do NOT invent api.netlify.com endpoints or read tokens from local CLI config files. Reference: https://open-api.netlify.com/#tag/submission/operation/listFormSubmissions

  • Page through results using the Link header — code that reads only the first response silently drops the rest.
  • listFormSubmissions returns data from old/removed fields no longer shown in the UI.
  • Query spam with ?state=spam.

Submission summary (field order matters)

The UI summary is derived from field type, not name:

  • Title: first non-hidden text <input> that isn't email-like (type="email", or name matching email/mail/from/twitter/sender); falls back to a field named title or subject.
  • Body: first <textarea>.

Field order in the HTML affects what appears in the summary.

Debugging missing submissions

  • First suspect: Akismet false positive. A missing legitimate submission is usually spam-flagged — check the Spam list (or API ?state=spam) and mark it verified. Do NOT build a custom recovery function or disable spam filtering as a first resort.
  • Test submissions get flagged as spam: use a real email (not [email protected]), write full sentences, don't hammer from one IP.
  • No submissions at all: confirm form detection is enabled and redeploy.
  • SSR/JS forms silently failing: verify the static skeleton file exists with exactly-matching field names and that AJAX targets the skeleton file, not /.
  • Missing old-field data: the UI shows only fields from the last deployed form version. Mark old fields hidden instead of removing them to keep them visible; old data remains available via listFormSubmissions.

Constraints

  • Deleting a form is permanent: future submissions return 404, past submissions become unavailable. Export CSV first.
  • Submitted code is sanitized (<script> → escaped entities).
  • For PII, export and delete data regularly.
  • Data is stored in Netlify's database, not accessible except via UI/API/CSV.

Netlify house rules (forms)

These are org conventions and field-learned guardrails, not docs facts — theyare merged into the rendered skill by ctx-gen and are never generated.Extracted from the previous hand-written netlify-forms skill; owned by theskills maintainer.

  1. In SSR apps (Next.js, Nuxt, SvelteKit, etc.), fetch("/") is interceptedby the SSR catch-all function and never reaches Netlify's form processing.POST the AJAX submission to the static skeleton file itself (e.g./__forms.html), not to an arbitrary path.
  2. Use only documented surfaces: do not curl https://api.netlify.com/...with an invented endpoint shape, and do not read tokens out of local CLIconfig files (~/Library/Preferences/netlify/config.json).
  3. When reading submissions via the API, page through results (Linkheader); code that reads only the first response silently drops the rest.
  4. For JS-rendered and SSR forms, always create the static skeleton filepublic/__forms.html: a hidden copy of each form withdata-netlify="true", a hidden form-name input, and every field thecomponent submits — names matching exactly (Netlify validates field namesagainst the registered form). Without this file, submissions silently fail.
  5. Astro routes rendered on demand (export const prerender = false, oroutput: "server" routes) are never scanned at build time, so their formsare never registered. Put the form on a prerendered page or rely on thestatic skeleton file.
  6. A "missing" legitimate submission is usually an Akismet false positive:check the Spam list (or the API with ?state=spam) and mark it verified.Do not build a custom recovery function or disable spam filtering as afirst resort.
  7. For custom success pages, use extensionless action paths (/thank-you,not /thank-you.html) — Netlify serves thank-you.html at /thank-youand the .html path returns 404.

すべてのファイル

0件のファイル

netlify-formsをインストール

スキルファイルをダウンロードし、.claude/skills/ ディレクトリに解凍してください。

ZIPをダウンロード

リポジトリをクローンし、スキルファイルをプロジェクトにコピーしてください。

git clone https://github.com/netlify/context-and-tools/blob/main/skills/netlify-forms/SKILL.md # Copy SKILL.md to your .claude/skills/ directory

コピー コピー
クイックセットアップ: スキルフォルダを .claude/skills/ にコピーしてください。Claude が自動的にスキルを検出して使用します。
リポジトリ netlify/context-and-tools

関連スキル

Cloudflare Manager
更新された時間 2026年6月29日
pinecone
更新された時間 2026年6月29日
azure-setup-guide
更新された時間 2026年6月29日
sentry-architecture-variants
更新された時間 2026年6月29日
OR