Ein Formular an deine E-Mail senden6 Min. Lesezeit
Eine EmailJS-Alternative, die Schlüssel vom Client fernhält
Lös ein Kontaktformular von EmailJS: warum E-Mail-Schlüssel im Browser ein Risiko sind, was sich im Code ändert und worauf du verzichtest, etwa die automatische Antwort.
EmailJS sendet E-Mails aus dem Browser, daher landen deine Service-ID, Template-ID und dein Public Key im JavaScript deiner Seite, wo jeder sie kopieren kann. Ein Formular-Backend braucht keinen Schlüssel im Browser: Das Formular sendet an eine URL, und das Backend filtert Spam, speichert die Nachricht und schickt dir eine E-Mail. Du verlierst die automatische Antwort von EmailJS an den Besucher, die du stattdessen aus deinem eigenen E-Mail-Tool senden würdest.
Was hier anders ist
EmailJS ist aus gutem Grund beliebt: Es sendet echte E-Mails von einer statischen Site ohne Server. Dazu ruft der Browser EmailJS direkt mit drei Werten auf: einer Service-ID, einer Template-ID und einem Public Key. Sie stecken in deinem Bundle, lesbar im Quelltext der Seite oder im Netzwerk-Tab.
Laut der FAQ von EmailJS kann jemand, der sie kopiert, nur deine Templates mit deinem Inhalt senden, keine beliebigen E-Mails. Das stimmt für den festen Text des Templates. Aber die Variablen des Templates füllt der Aufrufer, die kopierten Schlüssel können dein Template also mit beliebigem Namen, beliebiger Nachricht und beliebigen Links senden, so oft dein Kontingent es zulässt. Und wenn das To eines Templates eine Variable ist, wie bei einer automatischen Antwort an die Adresse des Besuchers, bestimmt der Aufrufer auch den Empfänger.
- Mit einem E-Mail-SDK im Browser enthält die Seite alles, was zum Senden nötig ist. Ein Skript an anderer Stelle kann die IDs und den Schlüssel kopieren und dein Template mit eigenem Text senden, bis zu deinem Kontingent.
- Kommt der Empfänger des Templates aus einer Variable, bestimmen die kopierten Schlüssel auch, wer es bekommt.
- Mit einem Formular-Endpunkt enthält die Seite nur eine Formular-ID. Posts werden auf Spam gefiltert und per Rate-Limit begrenzt, und E-Mails gehen nur an die Empfänger, die du im Dashboard festlegst.
EmailJS hat Einstellungen, die das eingrenzen, etwa die Begrenzung, welche Domains deinen Schlüssel nutzen dürfen, und ein Captcha für ein Template. Prüfe, welche dein Tarif enthält, und aktiviere sie, wenn du bleibst. Der Vergleich unten betrifft das Design, kein Urteil über den Dienst:
| Item | EmailJS | Ikiform |
|---|---|---|
| Was der Browser enthält | Service-ID, Template-ID, Public Key | Eine Formular-ID |
| Wer den Empfänger bestimmt | Das Template, das eine Variable lesen kann | Nur deine Dashboard-Einstellungen |
| Spamfilter | Captcha pro Template, wenn du eins hinzufügst | Honeypot, Spam-Bewertung und Rate-Limits bei jedem Formular, plus eine optionale Menschlichkeitsprüfung |
| Deine Nachrichten danach | In deinem Postfach | Im Dashboard, im CSV-Export und in der API sowie in deinem Postfach |
| E-Mail an den Besucher | Ja, mit einem Template für automatische Antworten | Nein |
| Dein eigenes E-Mail-Design | Ja, Templates in deren Editor | Nein, ein festes Layout mit dem Betreff New submission: <form name> |
Wenn es dir um die automatische Antwort oder eine gebrandete E-Mail geht, passt EmailJS oder dein eigener Server mit einer E-Mail-API besser. Wenn du die Nachricht sicher in deinem Postfach und irgendwo aufbewahrt haben willst, passt ein Formular-Backend.
Das Formular umziehen
Ein typisches EmailJS-Formular in React, basierend auf deren Kontaktformular-Tutorial:
import emailjs from "@emailjs/browser";
import { type FormEvent, useRef, useState } from "react";
export function ContactForm() {
const form = useRef<HTMLFormElement>(null);
const [status, setStatus] = useState("");
async function onSubmit(event: FormEvent<HTMLFormElement>) {
event.preventDefault();
if (!form.current) return;
try {
await emailjs.sendForm("service_abc123", "template_xyz789", form.current, {
publicKey: "aBcDeFgHiJkLmNoPq",
});
setStatus("Message sent.");
} catch {
setStatus("Couldn't send your message.");
}
}
return (
<form ref={form} onSubmit={onSubmit}>
<input name="user_name" required />
<input name="user_email" type="email" required />
<textarea name="message" required />
<button>Send</button>
<p role="status">{status}</p>
</form>
);
}Dasselbe Formular, das mit submit() aus @ikiform/sdk an Ikiform sendet:
import { submit } from "@ikiform/sdk";
import { type FormEvent, useState } from "react";
const FORM_ID = "frm_xxxxxxxxxxxxxxxx";
export function ContactForm() {
const [status, setStatus] = useState<"idle" | "sending" | "sent">("idle");
const [fields, setFields] = useState<Record<string, string[]>>({});
const [error, setError] = useState("");
async function onSubmit(event: FormEvent<HTMLFormElement>) {
event.preventDefault();
setStatus("sending");
setFields({});
setError("");
const result = await submit(FORM_ID, new FormData(event.currentTarget));
if (result.ok) {
setStatus("sent");
return;
}
setFields(result.error.fields ?? {});
setError(result.error.code === "network_error" ? "You're offline. Try again." : result.error.message);
setStatus("idle");
}
if (status === "sent") return <p role="status">Message sent. We'll reply by email.</p>;
return (
<form onSubmit={onSubmit}>
<label htmlFor="user_name">Name</label>
<input id="user_name" name="user_name" required />
<label htmlFor="email">Email</label>
<input id="email" name="email" type="email" required />
{fields.email ? <p role="alert">{fields.email[0]}</p> : null}
<label htmlFor="message">Message</label>
<textarea id="message" name="message" required />
{fields.message ? <p role="alert">{fields.message[0]}</p> : null}
<input name="_gotcha" type="text" tabIndex={-1} autoComplete="off" hidden />
{error && !Object.keys(fields).length ? <p role="alert">{error}</p> : null}
<button disabled={status === "sending"}>{status === "sending" ? "Sending" : "Send"}</button>
</form>
);
}@emailjs/browserund die drei IDs fallen weg. Deinstalliere das Paket und lösche das EmailJS-Template oder den Service, sobald das neue Formular funktioniert, damit gecachte Kopien deiner Seite nicht mehr senden.user_emailwird zuemail. EmailJS sind die Namen egal, aber Ikiform nutzt das Feld namensemailals Reply-To. Die anderen Namen können bleiben, weil Benachrichtigungen die Felder so auflisten, wie sie ankommen.submit()wirft nie. Es gibt den Fehler-Umschlag zurück, sodass Feldfehler aus der Definition des Formulars unter ihrem Eingabefeld erscheinen.- Der Honeypot ist neu. EmailJS hatte keinen; dieses versteckte Eingabefeld fängt die einfachsten Bots ohne jede Einrichtung ab.
- Füge deine Domain unter Allowed domains hinzu, damit Posts von anderen Sites als Spam markiert werden.
submit challenge(FORM_ID) auf und übergib das Token als turnstileToken. Die React-Anleitung zeigt das.Wenn du die automatische Antwort genutzt hast
Ikiform schickt der Person, die das Formular ausfüllt, keine E-Mail. Benachrichtigungs-E-Mails gehen nur an dich und dein Team, es gibt also keinen Autoresponder, keine Bestätigungs-E-Mail und kein Double-Opt-in. Verschicke sie aus deinem E-Mail-Tool, ausgelöst durch einen Webhook. Ein Versanddienst, den du von einem Server aus steuerst, etwa Resend oder Postmark, kann sie bei jedem submission.created-Webhook senden, mit der Adresse des Besuchers aus der vertrauenswürdigen Payload statt aus dem Browser. Die Anleitung zu mehreren Empfängern hat einen Handler, den du anpassen kannst.
Frag dich, ob du sie brauchst. Die Dankesnachricht eines Kontaktformulars auf der Seite sagt dem Besucher, dass es geklappt hat, und eine echte Antwort von dir eine Stunde später schlägt jedes Template.
Prüfen, ob es funktioniert
- Füge unter Allowed domains des Formulars die Domain deiner Site hinzu, plus
localhost, während du entwickelst. Lass das Feld leer, um Posts von überall anzunehmen. - Durchsuche deine gebaute Site nach
emailjsund deiner alten Service-ID. Nichts sollte gefunden werden. - Füll das Formular auf deiner Seite aus und sende es ab. Du landest auf deiner Dankeseite oder der gehosteten von Ikiform.
- 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.
Wenn etwas schiefgeht
| Problem | Was zu tun ist |
|---|---|
| Reply geht immer noch an Ikiform | Das Feld heißt noch user_email. Benenne es in email um oder in eins von email, email_address, your_email, reply_to, contact_email. |
422 validation_failed | Der Post passt nicht zur Felddefinition des Formulars. error.fields nennt jedes Feld und den Grund. Gleiche den name des Eingabefelds an die Feld-ID an. |
403 challenge_required | Die Spamprüfung des Formulars steht auf Ikiform, und ein fetch-Post hatte kein Token. Hol dir vor jedem Absenden eins mit challenge(formId) aus @ikiform/sdk oder schalte die Prüfung aus. |
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. |
Fragen
Das hängt von Volumen und Tarif ab, und beides ändert sich mit der Zeit, also vergleiche die aktuellen Preisseiten. Ikiform hat keinen kostenlosen Tarif; abgerechnet wird pro Einsendung, mit Pro oder einmaligen Paketen.
EmailJS sagt, ein kopierter Schlüssel kann nur deine Templates senden, keine eigenen E-Mails. Die Variablen des Templates füllt aber trotzdem der Aufrufer, und ist der Empfänger eine Variable, wählt er auch den. Beschränke den Schlüssel auf deine Domains und füge ein Captcha hinzu, wenn du ihn weiter nutzt.
Nein. Ein Browser-Formular sendet ohne Schlüssel an die URL deines Formulars. Secret Keys gibt es nur für das Senden von deinem eigenen Server, und sie gehören in serverseitige Umgebungsvariablen.
Nein. Ikiform schickt E-Mails an dich und dein Team, nicht an den Absender, es gibt also keinen Autoresponder und keine Bestätigungs-E-Mail. Sende einen Webhook an dein E-Mail-Tool und verschicke die Bestätigung von dort.
Ja, bis auf das E-Mail-Feld: Nenne es email, damit Antworten auf Benachrichtigungen an den Besucher gehen. Ikiform speichert jeden Feldnamen, den du sendest.
Hängst du bei einem Schritt fest oder willst eine Anleitung für deinen Fall? Schreib an hello@ikiform.com.