Kontaktformular auf einer statischen Site6 Min. Lesezeit
Ein Kontaktformular auf Netlify ohne Netlify Forms einbauen
Betreib ein Kontaktformular auf einer Netlify-Site ohne Netlify Forms: keine Erkennung beim Build, JSON-Posts aus React und ein eigenes Formular für Deploy Previews.
Lass data-netlify am Formular weg und richte seine action auf deinen Ikiform-Endpunkt aus; Netlify liefert dann die Seite aus und rührt den Post nie an. React- und Vue-Formulare brauchen keine versteckte HTML-Kopie und können JSON senden. Setze in netlify.toml eine andere Formular-ID für Deploy Previews, weil Preview-Hosts unter Allowed domains nicht zu deiner Site passen.
Was bei Netlify anders ist
Netlify ist der einzige große statische Host mit eingebauter Formularfunktion. Netlify Forms liest nach jedem Build dein deploytes HTML, findet Formulare mit data-netlify oder netlify und nimmt für diese Formulare Posts an deine eigene Site an. Jeder andere Post an eine statische Seite scheitert, und auf Netlify scheitert er mit 404 statt 405:
curl -i -X POST -d "message=hello" https://your-site.netlify.app/contact/HTTP/2 404
server: Netlify
cache-status: "Netlify Edge"; fwd=method; fwd-status=404
content-length: 0Dieser 404 schickt Leute auf die Suche nach einer fehlenden Seite, die gar nicht fehlt. Er bedeutet, dass kein erkanntes Formular zum Post gepasst hat. Ob du Netlify Forms überhaupt nutzen solltest, ist eine andere Frage, und der Vergleich Ikiform vs. Netlify Forms beantwortet sie ehrlich: Wenn deine Site immer auf Netlify bleibt und das Formular einfaches HTML ist, macht Netlify Forms weniger Arbeit. Diese Seite ist für die anderen Fälle.
| Deine Situation | Warum Netlify Forms im Weg ist |
|---|---|
| Das Formular wird von React, Vue oder Svelte gerendert | Der Build analysiert statisches HTML, daher braucht ein clientseitig gerendertes Formular ein verstecktes Duplikat, das Netlify finden kann |
Du willst JSON per fetch senden | Netlify Forms nimmt nur urlencoded- und Multipart-Bodys an |
| Die Site zieht vielleicht zu einem anderen Host um | Das Formular sendet an deine eigene Domain, funktioniert also nur, solange Netlify diese Domain ausliefert |
| Nachrichten sollen einen Webhook erreichen, den du verifizierst | Ikiform signiert jede Zustellung mit Standard-Webhooks-Headern und wiederholt sie bei Bedarf |
- Mit Netlify Forms durchsucht Netlify das HTML jedes Deployments nach Formularen mit data-netlify.
- Ein gefundenes Formular nimmt Posts an deine eigene Site an, und Netlify speichert sie.
- Ein nicht gefundenes Formular, etwa eines, das ohne versteckte Kopie per JavaScript gerendert wird, bekommt bei jedem Post 404.
- Mit Ikiform muss beim Build nichts gefunden werden.
- Das Formular sendet direkt an ikiform.com, als HTML oder JSON, und Ikiform speichert es und schickt dir eine E-Mail.
Ein Formular von Netlify Forms lösen
- Entferne
data-netlifybzw.netlify,netlify-honeypotunddata-netlify-recaptchavom Formular sowie ein eventuelles verstecktesform-name-Eingabefeld. Bleibtform-namedrin, wird es als normales Feld gespeichert. - Lösche die versteckte statische Kopie eines JavaScript-Formulars, wie die unten. Netlify brauchte sie nur, um das Formular zu erkennen.
- Setze die
actionoder diefetch-URL auf deinen Ikiform-Endpunkt. Die alteaction="/thanks"wandert in die Einstellung Redirect after submit des Formulars. - Lade vorher die bisherigen Einsendungen auf der Seite Forms von Netlify herunter. Sie ziehen nicht von allein um.
<form name="contact" netlify hidden>
<input name="name" />
<input name="email" />
<textarea name="message"></textarea>
</form>Lass form detection an, wenn andere Formulare auf der Site noch Netlify Forms nutzen. Laut Netlifys Dokumentation ist das Abschalten der Erkennung nur für Sites gedacht, die Netlify Forms nicht nutzen. Sobald nichts mehr darauf angewiesen ist, sparst du durch das Abschalten bei jedem Deployment einen Scan.
Was die Spam-Einstellungen von Netlify ersetzt
Netlify lässt Akismet über die Einsendungen laufen und bietet netlify-honeypot und reCAPTCHA. Bei Ikiform funktioniert der Honeypot weiter, wenn du Honeypot field des Formulars auf den Namen setzt, den du schon verwendest, etwa bot-field. Spam-Bewertung läuft ohne Einrichtung bei jedem Post, und Spam check auf Ikiform ersetzt reCAPTCHA durch Cloudflare Turnstile, ganz ohne Schlüssel. Entferne beim Wechsel das reCAPTCHA-div.
Ein React-Formular, das JSON sendet
Ohne Netlify Forms im Weg braucht ein clientseitig gerendertes Formular kein Duplikat und kann JSON senden, was Typen erhält und dich Werte vorher trimmen lässt. Dieses hier hält außerdem fest, welcher Netlify-Kontext die Seite gebaut hat:
import { type FormEvent, useState } from "react";
const ENDPOINT = `https://ikiform.com/f/${import.meta.env.VITE_IKIFORM_FORM_ID}`;
interface Message {
name: string;
email: string;
message: string;
context: string;
}
export function ContactForm() {
const [sent, setSent] = useState(false);
const [error, setError] = useState("");
async function onSubmit(event: FormEvent<HTMLFormElement>) {
event.preventDefault();
const data = new FormData(event.currentTarget);
const body: Message & Record<string, string> = {
name: String(data.get("name") ?? "").trim(),
email: String(data.get("email") ?? "").trim(),
message: String(data.get("message") ?? "").trim(),
context: import.meta.env.VITE_CONTEXT ?? "local",
_gotcha: String(data.get("_gotcha") ?? ""),
};
setError("");
const response = await fetch(ENDPOINT, {
method: "POST",
headers: { "Content-Type": "application/json", Accept: "application/json" },
body: JSON.stringify(body),
}).catch(() => null);
const result = await response?.json().catch(() => null);
if (result?.ok) return setSent(true);
const fields: Record<string, string[]> = result?.error?.fields ?? {};
setError(Object.values(fields)[0]?.[0] ?? result?.error?.message ?? "Couldn't send. Try again.");
}
if (sent) return <p role="status">Got it. I'll answer from my own inbox, usually the same day.</p>;
return (
<form onSubmit={onSubmit}>
<label htmlFor="name">Name</label>
<input id="name" name="name" autoComplete="name" required />
<label htmlFor="email">Email</label>
<input id="email" name="email" type="email" autoComplete="email" required />
<label htmlFor="message">Message</label>
<textarea id="message" name="message" rows={6} minLength={10} required />
<input name="_gotcha" type="text" tabIndex={-1} autoComplete="off" hidden />
{error ? <p role="alert">{error}</p> : null}
<button>Send</button>
</form>
);
}Content-Type: application/jsonsagt Ikiform, dass es JSON parsen soll;Accept: application/jsonbittet um eine JSON-Antwort.VITE_CONTEXTist ein Beispielname. Netlify setztCONTEXTwährend des Builds; gib es in deinem Build-Befehl an Vite weiter oder lass das Feld weg.- Der Honeypot wird als JSON-Schlüssel gesendet. Behalte ihn im Body, auch wenn das Eingabefeld versteckt ist, damit Bots, die ihn ausfüllen, sich weiterhin selbst verraten.
Deploy Previews bekommen ihr eigenes Formular
Deploy Previews liegen auf Hosts wie deploy-preview-12--your-site.netlify.app. Das ist keine Subdomain von your-site.netlify.app, passt also unter Allowed domains nicht, und Posts aus Previews landen im Spam. Gib Previews und Branch-Deployments in netlify.toml ein eigenes Formular und setze den Content-Security-Policy-Header in derselben Datei:
[build]
command = "npm run build"
publish = "dist"
[context.production.environment]
VITE_IKIFORM_FORM_ID = "frm_xxxxxxxxxxxxxxxx"
[context.deploy-preview.environment]
VITE_IKIFORM_FORM_ID = "frm_yyyyyyyyyyyyyyyy"
[context.branch-deploy.environment]
VITE_IKIFORM_FORM_ID = "frm_yyyyyyyyyyyyyyyy"
[[headers]]
for = "/*"
[headers.values]
Content-Security-Policy = "default-src 'self'; form-action 'self' https://ikiform.com; connect-src 'self' https://ikiform.com"Lass Allowed domains des Preview-Formulars leer oder füge beim Testen den genauen Preview-Host hinzu. Das Produktionsformular bekommt deine Domain und your-site.netlify.app. Header in netlify.toml gelten für jeden Deploy-Kontext; wenn du eine _headers-Datei bevorzugst, muss sie im Publish-Verzeichnis landen.
netlify.app allein zu Allowed domains hinzu. Jede Netlify-Site würde passen, auch Kopien deines Formulars auf den Sites anderer Leute.Prüfen, ob es funktioniert
- Öffne ein Deploy Preview, sende ab und prüfe, ob die Nachricht im Preview-Formular landet.
- Merge, dann sende in der Produktion ab. Prüfe, ob die Nachricht im Produktionsformular landet und die Seite Forms von Netlify nichts Neues zeigt.
- Prüfe das Postfach jedes Benachrichtigungsempfängers. Jeder bekommt eine E-Mail mit den Feldern, und Reply geht an den Absender, wenn das Formular ein
email-Feld hat. - Sende einen Post mit ausgefülltem Honeypot,
_gotcha=bot. Er bekommt trotzdem eine Erfolgsantwort, aber die Einsendung wird als Spam gespeichert und löst weder E-Mail noch Webhook aus. - Nichts angekommen? Stell den Statusfilter im Dashboard auf Spam. Spam bekommt dieselbe Erfolgsantwort wie eine echte Einsendung.
Stolperfallen
| Problem | Was zu tun ist |
|---|---|
404 beim Absenden | Der Post ging an deine eigene Site. Ein gecachter Build hat vielleicht noch die alte action, oder ein fetch-Aufruf sendet noch an /. Durchsuche das Publish-Verzeichnis nach form-name. |
| Einsendungen tauchen noch in Netlify auf | Irgendein Formular auf der Seite sendet noch mit data-netlify oder netlify an deine eigene Site, also erkennt und speichert Netlify es weiterhin. Entferne das Attribut, setze die action auf Ikiform und deploye neu. |
404 not_found von Ikiform nur bei Previews | Der Deploy-Preview-Kontext hat keine VITE_IKIFORM_FORM_ID, daher endet die URL auf undefined. Füge sie unter [context.deploy-preview.environment] hinzu. |
403 origin_not_allowed, oder jeder Post landet im Spam | Die Domain der Seite steht nicht unter Allowed domains des Formulars. Füge sie hinzu, plus localhost für lokale Tests. Subdomains einer eingetragenen Domain sind erlaubt. |
422 files_not_supported | Das Formular hat ein <input type="file"> mit ausgewählter Datei. Datei-Uploads werden noch nicht unterstützt; entferne das Eingabefeld oder bitte stattdessen um einen Link. |
Fragen
Ja. Netlify verarbeitet nur Formulare, die es beim Build erkannt hat. Ein Formular ohne data-netlify, dessen action auf einen anderen Endpunkt zeigt, wird von Netlify wie jede Seite ausgeliefert und sendet direkt an diesen Endpunkt.
Netlify beantwortet einen POST an eine statische Seite mit 404, außer Netlify Forms hat das Formular im deployten HTML erkannt. Markiere das Formular entweder für Netlify Forms oder richte seine action auf ein anderes Backend aus.
Ja. Die Erkennung erfolgt pro Formular, anhand des Attributs. Behalte data-netlify bei den Formularen, die Netlify verarbeiten soll, und lass die Formularerkennung an.
Sie bleiben bei Netlify, nicht bei Ikiform. Lade sie auf der Forms-Seite der Site herunter, bevor du die Formularerkennung abschaltest, und bewahre die Datei bei deinen Unterlagen auf.
Noch nicht. Ein Post mit einer Datei wird mit 422 files_not_supported abgelehnt. Bitte stattdessen um einen Link zur Datei, etwa die URL eines geteilten Laufwerks.
Hängst du bei einem Schritt fest oder willst eine Anleitung für deinen Fall? Schreib an hello@ikiform.com.