Recevoir un formulaire par e-mail7 min de lecture
Une alternative à EmailJS qui garde les clés hors du navigateur
Quittez EmailJS pour un formulaire de contact : pourquoi les clés côté navigateur sont un risque, ce qui change dans le code, et ce que vous perdez, comme la réponse automatique.
EmailJS envoie les e-mails depuis le navigateur : votre id de service, votre id de modèle et votre clé publique sont donc livrés dans le JavaScript de votre page, à la portée de tous. Un backend de formulaires n'a besoin d'aucune clé dans le navigateur : le formulaire envoie vers une URL, et le backend filtre le spam, stocke le message et vous envoie un e-mail. Vous perdez la réponse automatique d'EmailJS au visiteur, que vous enverriez plutôt depuis votre propre outil d'e-mailing.
Ce qui change ici
EmailJS est populaire pour une bonne raison : il envoie de vrais e-mails depuis un site statique sans serveur. Pour cela, le navigateur appelle directement EmailJS avec trois valeurs : un id de service, un id de modèle et une clé publique. Elles sont dans votre bundle, lisibles dans le code source de la page ou l'onglet réseau.
La FAQ d'EmailJS indique que quelqu'un qui les copie ne peut envoyer que vos modèles avec votre contenu, pas des e-mails arbitraires. C'est vrai pour le texte fixe du modèle. Mais c'est l'appelant qui remplit les variables du modèle : les clés copiées peuvent donc envoyer votre modèle avec n'importe quel nom, message et liens, aussi souvent que votre quota le permet. Et si le To d'un modèle est une variable, comme pour une réponse automatique à l'adresse du visiteur, l'appelant choisit aussi le destinataire.
- Avec un SDK d'e-mail côté navigateur, la page contient tout ce qu'il faut pour envoyer. Un script ailleurs peut copier les id et la clé et envoyer votre modèle avec son propre texte, jusqu'à votre quota.
- Si le destinataire du modèle vient d'une variable, les clés copiées choisissent aussi qui le reçoit.
- Avec un endpoint de formulaire, la page ne contient qu'un id de formulaire. Les envois sont filtrés contre le spam et soumis à une limite de débit, et les e-mails ne vont qu'aux destinataires définis dans le tableau de bord.
EmailJS propose des réglages qui réduisent ce risque, comme restreindre les domaines autorisés à utiliser votre clé et ajouter un captcha à un modèle. Vérifiez lesquels votre offre inclut et activez-les si vous restez. La comparaison ci-dessous porte sur la conception, pas sur un verdict concernant le service :
| Item | EmailJS | Ikiform |
|---|---|---|
| Ce que contient le navigateur | Id de service, id de modèle, clé publique | Un id de formulaire |
| Qui décide du destinataire | Le modèle, qui peut lire une variable | Uniquement les réglages de votre tableau de bord |
| Filtrage du spam | Captcha par modèle, si vous l'ajoutez | Honeypot, score de spam et limites de débit sur chaque formulaire, plus une vérification humaine facultative |
| Vos messages ensuite | Dans votre boîte | Dans le tableau de bord, l'export CSV et l'API, en plus de votre boîte |
| E-mail au visiteur | Oui, avec un modèle de réponse automatique | Non |
| Votre propre design d'e-mail | Oui, des modèles dans leur éditeur | Non, une mise en page fixe avec l'objet New submission: <form name> |
Si la réponse automatique ou un e-mail à votre marque est l'essentiel, EmailJS, ou votre propre serveur avec une API d'e-mail, convient mieux. Si vous voulez le message dans votre boîte, en toute sécurité, et conservé quelque part, un backend de formulaires convient.
Migrer le formulaire
Un formulaire EmailJS typique en React, d'après leur tutoriel de formulaire de contact :
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>
);
}Le même formulaire qui envoie à Ikiform avec submit() de @ikiform/sdk :
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/browseret les trois id disparaissent. Désinstallez le package, et supprimez le modèle ou le service EmailJS une fois le nouveau formulaire en place, pour que les copies en cache de votre page cessent d'envoyer.user_emaildevientemail. EmailJS ne se soucie pas des noms, mais Ikiform utilise le champ appeléemailcomme adresse de réponse. Les autres noms peuvent rester, car les notifications listent les champs tels qu'ils arrivent.submit()ne lève jamais d'exception. Il renvoie l'enveloppe d'erreur, pour que les erreurs de champ issues de la définition du formulaire s'affichent sous leur input.- Le honeypot est nouveau. EmailJS n'en avait pas ; cet input masqué arrête les bots les plus simples sans configuration.
- Ajoutez votre domaine dans Allowed domains, pour que les envois depuis d'autres sites soient marqués comme spam.
challenge(FORM_ID) avant submit et passez le jeton en turnstileToken. Le guide React le montre.Si vous utilisiez la réponse automatique
Ikiform n'envoie pas d'e-mail à la personne qui remplit le formulaire. Les e-mails de notification vont uniquement à vous et à votre équipe : il n'y a donc ni réponse automatique, ni e-mail de confirmation, ni double opt-in. Envoyez-les depuis votre outil d'e-mailing, déclenché par un webhook. Un service d'envoi que vous pilotez depuis un serveur, comme Resend ou Postmark, peut l'envoyer à chaque webhook submission.created, avec l'adresse du visiteur tirée du payload de confiance plutôt que du navigateur. Le guide sur les destinataires multiples contient un gestionnaire que vous pouvez adapter.
Demandez-vous si vous en avez besoin. Le message de remerciement sur la page indique au visiteur que cela a fonctionné, et une vraie réponse de votre part une heure plus tard vaut mieux qu'un modèle.
Vérifiez que tout fonctionne
- Dans Allowed domains du formulaire, ajoutez le domaine de votre site, plus
localhostpendant le développement. Laissez vide pour accepter les envois de partout. - Cherchez
emailjset votre ancien id de service dans votre site compilé. Rien ne doit correspondre. - Remplissez le formulaire sur votre page et envoyez-le. Vous arrivez sur votre page de remerciement, ou sur celle hébergée par Ikiform.
- Vérifiez la boîte de réception de chaque destinataire des notifications. Chacun reçoit un e-mail avec les champs, et Reply répond à l'expéditeur quand le formulaire a un champ
email. - Envoyez avec le honeypot rempli,
_gotcha=bot. L'envoi reçoit quand même une réponse de succès, mais la soumission est stockée comme spam et n'envoie ni e-mail ni webhook.
Si quelque chose ne va pas
| Problème | Que faire |
|---|---|
| Répondre écrit toujours à Ikiform | Le champ s'appelle toujours user_email. Renommez-le email, ou l'un de email, email_address, your_email, reply_to, contact_email. |
422 validation_failed | L'envoi ne correspond pas à la définition des champs du formulaire. error.fields nomme chaque champ et la raison. Faites correspondre le name de l'input à l'id du champ. |
403 challenge_required | Le spam check du formulaire est réglé sur Ikiform et un envoi fetch n'avait pas de jeton. Obtenez-en un avec challenge(formId) de @ikiform/sdk avant chaque envoi, ou désactivez la vérification. |
403 origin_not_allowed, ou tous les envois finissent en spam | Le domaine de la page n'est pas dans les Allowed domains du formulaire. Ajoutez-le, plus localhost pour les tests en local. Les sous-domaines d'un domaine listé sont autorisés. |
Questions
Cela dépend du volume et de l'offre, et les deux évoluent : comparez les pages de tarifs actuelles. Ikiform n'a pas d'offre gratuite ; il facture à la soumission, avec Pro ou des packs payés une seule fois.
EmailJS indique qu'une clé copiée ne peut envoyer que vos modèles, pas des e-mails personnalisés. Mais l'appelant remplit quand même les variables du modèle, et si le destinataire est une variable, il le choisit aussi. Restreignez la clé à vos domaines et ajoutez un captcha si vous continuez à l'utiliser.
Non. Un formulaire côté navigateur envoie vers l'URL de votre formulaire sans clé. Les clés secrètes ne servent qu'à envoyer depuis votre propre serveur, et elles ont leur place dans des variables d'environnement côté serveur.
Non. Ikiform envoie des e-mails à vous et à votre équipe, pas à l'expéditeur : il n'y a donc ni réponse automatique ni e-mail de confirmation. Envoyez un webhook à votre outil d'e-mailing et envoyez la confirmation depuis celui-ci.
Oui, sauf le champ e-mail : nommez-le email pour que les réponses aux notifications aillent au visiteur. Ikiform stocke tous les noms de champs que vous envoyez.
Une étape vous bloque, ou vous voulez un guide pour votre cas ? Écrivez à hello@ikiform.com.