Menu
Guide pratique

Créer un formulaire de contact JavaScript avec fetch

Votre formulaire HTML fonctionne déjà, mais vous souhaitez afficher la confirmation dans la page ? JavaScript peut intercepter son envoi avec fetch. Il faut toujours une destination pour recevoir les champs : Formeo fournit ce traitement sans backend à développer sur votre site.

Créer mon formulaire gratuitement

Un exemple à copier

HTML + JavaScript natif
<form action="https://formeo.me/api/submit" method="POST" id="contact-js" data-locale="fr" data-success="Votre message a bien été enregistré. Nous vous répondrons bientôt.">
  <input type="hidden" name="_fq_key" value="YOUR_ACCESS_KEY" />
  <input type="text" name="botcheck" tabindex="-1" autocomplete="off" style="display:none" aria-hidden="true" />

  <label for="fq_name">Votre nom *</label>
  <input type="text" name="name" id="fq_name" required autocomplete="name" />

  <label for="fq_email">Votre adresse e-mail *</label>
  <input type="email" name="email" id="fq_email" required autocomplete="email" />

  <label for="fq_message">Votre message *</label>
  <textarea name="message" id="fq_message" required maxlength="5000" rows="5"></textarea>

  <button type="submit">Envoyer mon message</button>
  <p role="status" aria-live="polite"></p>
</form>
<script>
const form = document.getElementById('contact-js');
const button = form.querySelector('button[type="submit"]');
const status = form.querySelector('[role="status"]');
let attempt = { content: '', id: '', accepted: false };
let busy = false;
function readBody() {
  const body = new URLSearchParams();
  for (const [name, value] of new FormData(form)) body.append(name, String(value));
  return body;
}
form.addEventListener('input', () => {
  if (!busy) {
    button.disabled = attempt.accepted && attempt.content === readBody().toString();
    if (!button.disabled) status.textContent = '';
  }
});
form.addEventListener('submit', async (event) => {
  event.preventDefault();
  if (busy || !form.reportValidity()) return;
  const body = readBody();
  const content = body.toString();
  if (content !== attempt.content) attempt = { content, id: crypto.randomUUID(), accepted: false };
  if (attempt.accepted) return;
  busy = true;
  button.disabled = true;
  status.textContent = 'Envoi en cours…';
  try {
    const response = await fetch(form.action, {
      method: 'POST',
      headers: { Accept: 'application/json', 'Idempotency-Key': attempt.id },
      body
    });
    const result = await response.json();
    if (!response.ok) throw new Error(response.status === 429
      ? 'Limite atteinte. Attendez avant de réessayer.'
      : 'Envoi impossible. Vérifiez les champs et réessayez.');
    if (!result.data) throw new Error('Confirmation illisible. Vérifiez la réception avant de réessayer.');
    attempt.accepted = true;
    status.textContent = 'Votre message a été enregistré. La livraison e-mail est suivie séparément.';
  } catch (error) {
    status.textContent = error instanceof Error ? error.message : 'Connexion impossible. Réessayez.';
  } finally {
    busy = false;
    button.disabled = attempt.accepted && attempt.content === readBody().toString();
  }
});
</script>

La clé YOUR_ACCESS_KEY est un emplacement à remplacer par la clé publique de votre formulaire. Le code ne contient aucune clé serveur ni adresse de destination.

Partir d’un formulaire HTML qui fonctionne

Gardez les attributs action et method=POST, un nom pour chaque champ et un bouton de type submit. Sans JavaScript, le navigateur peut ainsi transmettre le formulaire normalement. L’exemple ajoute le script après le formulaire, quand les éléments sont disponibles dans le document.

Créez un formulaire de contact avec les champs name, email et message dans Formeo, publiez-le et remplacez YOUR_ACCESS_KEY par sa clé publique. Configurez les destinataires dans votre compte : aucune adresse de réception ni clé de gestion ne doit être ajoutée au JavaScript public.

Comprendre fetch, FormData et les réponses HTTP

FormData lit les valeurs des contrôles nommés. Notre exemple encode les champs texte dans URLSearchParams et demande une réponse JSON. Le navigateur calcule lui-même le Content-Type du corps ; il n’est pas nécessaire d’installer une bibliothèque de formulaires.

fetch ne rejette pas automatiquement une réponse 422 ou 429. Vérifiez response.ok avant de montrer un succès. Un message accepté a été enregistré ; la réponse ne prouve pas encore que la notification e-mail est livrée. Les erreurs de connexion et les réponses illisibles doivent aussi laisser une possibilité de réessayer.

Préserver le message et éviter les doublons

Pendant la requête, désactivez uniquement le bouton d’envoi. Ne désactivez pas les champs avant de construire FormData : les contrôles désactivés ne sont pas inclus. Une erreur ne doit pas effacer le texte saisi. Une zone role=status annonce le résultat sans imposer une fenêtre d’alerte.

La même tentative conserve sa clé d’idempotence lorsque le contenu n’a pas changé, y compris après une coupure réseau. Une modification du message crée une nouvelle tentative. Après un succès, l’exemple attend une modification des champs avant de permettre un nouvel envoi identique. Gardez un seul gestionnaire d’envoi : n’ajoutez pas également le client Formeo à cet exemple personnalisé.

Tester avant de publier

Testez un message valide, une adresse e-mail invalide, une erreur réseau et un double clic. Vérifiez qu’un seul message apparaît dans votre espace, que le texte reste présent après l’erreur et que le bouton redevient utilisable. Faites ce contrôle depuis un serveur local ou un site HTTPS, pas seulement depuis un fichier ouvert en file://.

Ce petit exemple couvre les champs texte. Pour des fichiers ou un CAPTCHA, utilisez le parcours documenté et le code fourni par le tableau de bord : une pièce jointe n’est pas un simple champ JSON. Ne contournez pas une erreur CORS avec mode=no-cors, car vous ne pourriez plus lire la confirmation.

Sources

Questions fréquentes

JavaScript envoie-t-il lui-même les e-mails ?

Non. Il transmet les champs par HTTP. Le backend reçoit la demande et prépare la notification vers les destinataires configurés.

Faut-il choisir React pour utiliser fetch ?

Non. Cet exemple fonctionne en JavaScript natif dans une page HTML. Les intégrations React et Next.js expliquent leurs propres états et événements.

Puis-je supprimer action et method ?

Gardez-les pour que l’envoi HTML reste possible quand le script n’est pas disponible. Le parcours de confirmation natif diffère de la confirmation dans la page.

Passer de l’exemple à votre formulaire

Créer mon formulaire gratuitement