Kontaktformular auf Vercel ohne Backend
Kontaktformular für deine Seite auf Vercel, ohne eigene Function: ein HTML-Formular oder fetch(), E-Mail-Benachrichtigung, Spamschutz ohne Captcha.
Vercel liefert deine Seite aus und führt Functions aus, die du selbst schreibst, sammelt aber keine Formular-Einsendungen für dich. Ein <form> auf einer Vercel-Seite braucht einen Endpoint, der den POST annimmt, die Nachricht speichert und sie dir mailt. Den kannst du als Vercel Function selbst schreiben, oder du richtest das Formular auf ein gehostetes Formular-Backend und deployst nichts zusätzlich. Bei Formlane ist dieser Endpoint https://smallwerk.com/formlane/api/submit. Das funktioniert gleich für reines HTML, Astro, Hugo, Vite/React, SvelteKit oder Next.js auf Vercel: keine API-Route, kein Secret in den Umgebungsvariablen, keine Änderung an vercel.json. Einsendungen werden in Frankfurt gespeichert und an die Adresse deines Kontos gemailt, mit Reply-To auf den Absender.
So geht's
- Melde dich bei Formlane an, leg ein Formular an und kopiere den Access Key (
fl_…). - Füg das Formular unten in deine Kontaktseite ein und ersetze
fl_YOUR_ACCESS_KEYdurch deinen Key. Der Key ist absichtlich öffentlich (mit ihm kann man nur an dieses eine Formular senden), er darf also im HTML oder im Client-Code stehen. - Setz das versteckte Feld
redirectauf eine Danke-Seite auf deiner eigenen Domain, z. B.https://example.com/thanks. - Beschränke das Formular in den Einstellungen auf deine Produktions-Domain, damit Formulare auf anderen Websites nicht daran senden können.
- Push in dein Git-Repository (oder führ
vercelaus), öffne die veröffentlichte Seite, schick eine Testnachricht und prüf dein Postfach und das Formlane-Dashboard.
Code
<form action="https://smallwerk.com/formlane/api/submit" method="POST"> <input type="hidden" name="access_key" value="fl_YOUR_ACCESS_KEY"> <input type="hidden" name="subject" value="New message from my Vercel site"> <input type="hidden" name="redirect" value="https://example.com/thanks"> <!-- Honeypot: hidden from people, leave it unchecked --> <input type="checkbox" name="botcheck" style="display:none" tabindex="-1" autocomplete="off"> <label for="name">Name</label> <input id="name" type="text" name="name" required> <label for="email">Email</label> <input id="email" type="email" name="email" required> <label for="message">Message</label> <textarea id="message" name="message" rows="5" required></textarea> <button type="submit">Send</button> </form>
Funktioniert als Kontaktseite jedes Frameworks, das du auf Vercel deployst. Das Feld email wird zur Reply-To-Adresse der Benachrichtigung, du antwortest also mit einem Klick. Die Beschriftungen im Snippet (Name, Email, Message, Send) sind englisch; für eine deutsche Seite ersetzt du sie, die name-Attribute bleiben.
Optional: auf der Seite bleiben
Lieber eine Bestätigung ohne Seitenwechsel? Schick dasselbe Formular per fetch(). message in der Antwort ist immer englisch, deshalb zeigt das Snippet je nach error-Code einen eigenen Text. Die Texte im Snippet sind englisch; ersetz sie für eine deutsche Seite:
<script>
const MESSAGES = {
limit_reached: "This form can't take more messages this month. Please email us directly.",
rate_limited: "Too many messages in a short time. Please try again in a few minutes.",
origin_not_allowed: "This form only accepts messages from its own website.",
};
const form = document.querySelector('form[action*="formlane"]');
if (form) form.addEventListener("submit", async (event) => {
event.preventDefault();
// FormData skips the unchecked honeypot checkbox, so real visitors never send it.
const data = Object.fromEntries(new FormData(form));
try {
const res = await fetch(form.action, {
method: "POST",
headers: { "Content-Type": "application/json", Accept: "application/json" },
body: JSON.stringify(data),
});
const json = await res.json();
form.outerHTML = json.success
? "<p>Thanks! Your message has been sent.</p>"
: "<p>" + (MESSAGES[json.error] || "Sorry, that didn't work. Please try again later.") + "</p>";
} catch {
alert("Network error. Please try again.");
}
});
</script>Du nutzt React, Vite oder Next.js? Der React-Guide hat eine fertige Komponente, der Next.js-Guide zeigt dasselbe ohne API-Route.
So sieht die Antwort aus
Mit Accept: application/json antwortet Formlane mit JSON:
- Erfolg (HTTP 200):
{"success": true, "message": "Form submitted successfully", "id": "…"} - Fehler (4xx/5xx):
{"success": false, "message": "…", "error": "…"}. Fehlercodes sind unter andereminvalid_key,origin_not_allowed,rate_limitedundlimit_reached.
Ein reines HTML-Formular (ohne Accept: application/json) wird nach einer erfolgreichen Einsendung auf deine redirect-URL weitergeleitet.
Grenzen: 64 KB pro Einsendung, 50 Felder, 10.000 Zeichen pro Feld, 10 Einsendungen in 10 Minuten pro Besucher und Formular. Datei-Uploads gehen noch nicht.
Hinweise für Vercel
- Keine Function in deinem Projekt: Das Formular sendet direkt aus dem Browser des Besuchers an Formlane. Du deployst keine Function, setzt kein Secret, und dein Vercel-Projekt hat für das Formular keine Function-Nutzung.
- Preview-Deployments: Erlaubte Domains gelten für die Domain und ihre Subdomains. Vercel-Preview-URLs (zum Beispiel
my-site-git-feature-my-team.vercel.app) sind keine Subdomains deiner Produktionsadresse, ein Eintrag deckt sie also nicht ab. Teste auf deiner Produktions-Domain oder trag den genauen Preview-Hostnamen kurz ein. Trag nievercel.appallein ein: Dann wären Einsendungen von jeder Seite aufvercel.apperlaubt. - Eigene Domain: Setz das Feld
redirectauf deine eigene Domain, nicht auf die*.vercel.app-Adresse, damit Besucher bei deiner Marke bleiben. - Erlaubte Domains sind eine Browser-Prüfung. Sie verhindern, dass Formulare auf anderen Websites deinen Key nutzen. Ein Skript, das den
Origin-Header fälscht, kommt trotzdem durch; Honeypot, Rate-Limit und Inhaltsregeln greifen dann weiterhin.
Andere Wege (ehrlicher Vergleich)
| Weg | Gut für | Worauf du achten musst |
|---|---|---|
| Gehostetes Formular-Backend (Formlane, Formspree, Web3Forms und andere) | Kontakt- und Anfrageformulare auf jeder Seite bei Vercel; kein Code zum Deployen | Monatliche Kontingente; jeder Dienst hat einen eigenen Free-Plan und eigene Funktionen (Datei-Uploads, Webhooks, Autoresponder unterscheiden sich) |
| Eigene Vercel Function oder Next.js Server Action + eine E-Mail-API | Volle Kontrolle, eigene Logik, Daten bleiben in deinem Stack | Validierung, Spamschutz, Rate-Limit, Speicherung und E-Mail-Versand baust und pflegst du selbst |
| E-Mail aus dem Browser (z. B. EmailJS) | Eine E-Mail mit Vorlage direkt aus dem Client-Code senden | Nachrichten sind E-Mails, keine gespeicherte Liste; prüf, wie Spam behandelt wird |
| Netlify Forms | Seiten, die auf Netlify laufen | Funktioniert nur für Seiten auf Netlify, nicht auf Vercel |
Formlane passt, wenn du ein reines HTML-Formular auf Vercel willst, Einsendungen in Frankfurt gespeichert werden sollen und du Spamschutz ohne Captcha brauchst. Brauchst du heute Datei-Uploads, Autoresponder, Webhooks oder ein Captcha, nimm einen Dienst, der das schon kann: Formlane kann es noch nicht.
Preise
Free: 250 Einsendungen im Monat und bis zu 5 Formulare. Starter: 9 € im Monat für 2.500 Einsendungen. Pro: 19 € im Monat für 25.000 Einsendungen. Preise inkl. MwSt.
Weitere Anleitungen
Häufige Fragen
Hat Vercel eine eingebaute Formularverarbeitung?
Vercel hostet deine Seite und führt Functions aus, die du schreibst, sammelt aber keine Formular-Einsendungen für dich. Entweder schreibst du eine Function, die den POST annimmt, oder du richtest die action des Formulars auf ein Formular-Backend wie Formlane.
Brauche ich eine API-Route oder Vercel Function für ein Kontaktformular?
Nicht mit einem Formular-Backend. Das Formular sendet direkt an https://smallwerk.com/formlane/api/submit; dort wird die Nachricht gespeichert und an dich gemailt. Auf Vercel wird nichts zusätzlich deployt.
Darf der Access Key im HTML stehen?
Ja. Der Formlane-Access-Key ist absichtlich öffentlich und erlaubt nur Einsendungen an genau dieses Formular. Beschränke das Formular in den Einstellungen auf deine Domain, damit Formulare auf anderen Websites ihn nicht nutzen können.
Funktioniert das mit Next.js, Astro, SvelteKit oder reinem HTML auf Vercel?
Ja. Das Formular ist reines HTML (oder ein fetch()-Aufruf) und funktioniert deshalb mit jedem Framework, das Vercel hostet, auch bei einem komplett statischen Projekt.
Wie wird Spam gefiltert?
Mit einer versteckten Honeypot-Checkbox, einem Rate-Limit pro Besucher und einfachen Inhaltsregeln (zum Beispiel gegen Link-Fluten), ohne Captcha. Details im Guide Kontaktformular-Spam stoppen ohne Captcha.
Wo werden die Einsendungen gespeichert?
Einsendungen werden in Frankfurt gespeichert, in einer Supabase-Datenbank (Region eu-central-1). Die App läuft bei Vercel, Benachrichtigungen gehen über einen E-Mail-Dienstleister; alle Dienstleister stehen in der Datenschutzerklärung.
Was passiert beim Monatslimit?
Neue Einsendungen werden mit dem Fehler limit_reached abgelehnt, bis der nächste Kalendermonat beginnt oder du upgradest. Gespeicherte Einsendungen bleiben.
Access Key holen
Kostenlos für 250 Einsendungen pro Monat. Einsendungen werden in Frankfurt gespeichert.
Access Key holen