Menu
Intégration

Créer un formulaire de contact React

Un gestionnaire onSubmit récupère les champs avec FormData, puis appelle l’API publique. useState affiche le chargement et le résultat sans imposer une bibliothèque de formulaires.

Créer mon formulaire gratuitement

Un exemple à copier

React / JSX
// Composant React côté navigateur
import { useState, useRef } from 'react';
export default function Contact() {
  const [etat, setEtat] = useState('');
  const [busy, setBusy] = useState(false);
  const tentative = useRef({ contenu: '', id: '' });
  async function envoyer(event) {
    event.preventDefault();
    const form = event.currentTarget;
    if (!form.reportValidity()) return;
    const data = new FormData(form);
    const fields = { name: data.get('name'), email: data.get('email'), message: data.get('message') };
    const contenu = JSON.stringify(fields);
    if (contenu !== tentative.current.contenu) tentative.current = { contenu, id: crypto.randomUUID() };
    setBusy(true); setEtat("Envoi en cours…");
    try {
      const response = await fetch("https://formeo.me/api/submit", {
        method: 'POST', headers: { 'Content-Type': 'application/json', 'Idempotency-Key': tentative.current.id },
        body: JSON.stringify({ form_key: "YOUR_ACCESS_KEY", fields })
      });
      const result = await response.json();
      if (!response.ok) throw new Error(response.status === 429 ? "Limite atteinte. Réessayez plus tard." : "Envoi impossible. Vérifiez les champs et réessayez.");
      setEtat("Votre message a été enregistré. La livraison e-mail est suivie séparément.");
    } catch (error) { setEtat(error instanceof Error ? error.message : "Envoi impossible."); }
    finally { setBusy(false); }
  }
  return <form onSubmit={envoyer}>
    <label>Votre nom<input name='name' autoComplete='name' required /></label>
    <label>Votre e-mail<input name='email' type='email' autoComplete='email' required /></label>
    <label>Votre message<textarea name='message' required maxLength={5000} /></label>
    <button disabled={busy}>Envoyer le message</button>
    <p role='status' aria-live='polite'>{etat}</p>
  </form>;
}

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.

Champs contrôlés ou FormData

FormData convient à un formulaire simple avec des champs nommés. Si votre interface utilise des champs contrôlés, vous pouvez construire le même objet fields à partir de votre state.

Les noms name, email et message doivent correspondre à la configuration publiée. Le label visible peut être personnalisé sans changer le contrat d’envoi.

Prévenir les doubles clics

Désactivez le bouton pendant la requête et réutilisez la même clé d’idempotence lorsque le contenu n’a pas changé. Le verrou visuel seul ne couvre pas une réponse réseau perdue.

La réponse JSON possède data et error. Une erreur ne doit pas effacer la saisie du visiteur. L’exemple conserve les champs pour permettre une correction ou une reprise.

Installer dans votre application

Importez Contact dans votre page et remplacez YOUR_ACCESS_KEY par la clé publique copiée depuis le tableau de bord. Aucun mot de passe ni destinataire privé n’est nécessaire dans le code.

Pour plusieurs sites clients, utilisez un formulaire distinct par projet. Chaque formulaire conserve ses propres destinataires et ses règles.

Essayer le modèle avant de créer un compte

Aperçu local : les essais restent dans ce navigateur. Aucun message ni e-mail n’est envoyé.

Contact HTML

Nom, e-mail et message : le nécessaire pour ouvrir la conversation.

1 / 1
Formeo

Personnaliser les champs et le style

HTML / CSS autonome
<!doctype html>
<html lang="fr"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>Formulaire de contact</title>
<style>form[data-littlereply]{max-width:640px;margin:2rem auto;padding:1.5rem;font:16px/1.6 system-ui,sans-serif;color:#202033;background:white;border:1px solid #e4e4ee;border-radius:16px;box-sizing:border-box}form[data-littlereply] label{display:block;margin-top:1rem;font-weight:600}form[data-littlereply] input,form[data-littlereply] textarea,form[data-littlereply] select{box-sizing:border-box;width:100%;padding:.75rem;font:inherit;border:1px solid #727285;border-radius:8px;background:white;color:#202033}form[data-littlereply] input[type=checkbox],form[data-littlereply] input[type=radio]{width:auto}form[data-littlereply] button{margin-top:1rem;padding:.8rem 1.1rem;font:inherit;font-weight:600;border:0;border-radius:8px;background:var(--form-accent,#4f46e5);color:white;cursor:pointer}form[data-littlereply] :focus-visible{outline:3px solid #f59e0b;outline-offset:3px}form[data-littlereply] button:disabled{opacity:.6}form[data-littlereply] [role=status]{min-height:1.6em}form[data-littlereply] fieldset{border:0;padding:0}form[data-littlereply] .privacy-notice{font-size:.875rem;color:#4b4b60}
form[data-littlereply]{--form-accent:#4f46e5;--form-ink:#ffffff}
form[data-littlereply] button{color:var(--form-ink)}form[data-littlereply] button[data-fq-back]{background:#eee;color:#202033}form[data-littlereply] [hidden]{display:none}</style></head><body>
<form action="https://formeo.me/api/submit" method="POST" data-littlereply="YOUR_ACCESS_KEY" 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>
<!-- Erreurs AJAX accessibles et reprises sans doublon -->
<script src="https://formeo.me/client/v1.js" data-api="https://formeo.me" defer></script>
</body></html>

Remplacez YOUR_ACCESS_KEY par la clé publique de votre propre formulaire. Le code inclut ses styles ; le script client ajoute les états AJAX. Le POST HTML fonctionne aussi sans JavaScript.

Sources

Questions fréquentes

Ai-je besoin de React Hook Form ?

Non. Pour ce modèle simple, le contrôle natif et FormData suffisent. Vous pouvez utiliser une bibliothèque si votre application en a déjà une.

Quelle différence avec l’exemple Next.js ?

Le contrat HTTP est identique ; Next.js exige la frontière client pour les événements et les hooks de cet exemple.

Passer de l’exemple à votre formulaire

Créer mon formulaire gratuitement