Aller au contenu

Comment créer un formulaire de contact dans React Native (et Expo) qui envoie des e-mails

Un formulaire de contact React Native qui vous envoie chaque message par e-mail, avec état d’envoi, protection contre les bots et sans backend. Fonctionne avec Expo et React Native nu.

Mis à jour:

La vidéo est en anglais ; le texte ci-dessous est le tutoriel complet.

Vous allez ajouter un formulaire de contact à une application React Native ou Expo avec le paquet @frontmail/react-native : un provider, un hook useSendEmail qui suit l’état de l’envoi et un composant Turnstile prêt à l’emploi contre les bots. L’e-mail part via votre propre boîte mail.

Il vous faut : un compte Frontmail gratuit, un projet Expo ou React Native et une dizaine de minutes.

1. Connectez votre boîte mail et créez le modèle

  1. Services d’envoi → Ajouter un service : connectez Gmail, Outlook, SMTP ou un fournisseur transactionnel, cochez Définir par défaut, puis Vérifier et enregistrer.
  2. Modèles → Depuis la galerie → Formulaire de contact → Utiliser ce modèle. Paramètres obligatoires : name, email, message ; facultatifs : phone, topic.
  3. Copiez l’ID du modèle (tpl_…) depuis l’onglet Extraits de code.

2. Autorisez les applications mobiles

  • Sécurité → Applications mobiles → Autoriser les applications mobiles → Enregistrer. Les applications n’envoient pas d’en-tête Origin : sans ce réglage, l’API répond origin_not_allowed.
  • Modèle → Paramètres → Protection → Exiger un CAPTCHA (Turnstile). L’application utilise le widget Turnstile partagé de Frontmail ; vous n’avez pas besoin de clés Cloudflare.

3. Installez

npx expo install @frontmail/react-native @react-native-async-storage/async-storage react-native-webview

Dans un projet React Native nu, utilisez npm i avec les mêmes paquets, puis lancez npx pod-install.

4. Créez le formulaire

import { useRef, useState } from 'react';
import { Button, Text, TextInput, View } from 'react-native';
import { FrontmailProvider, TurnstileWebView, useSendEmail, type TurnstileWebViewHandle } from '@frontmail/react-native';

const options = { publicKey: 'pk_…' }; // keep the object stable

export default function App() {
  return (
    <FrontmailProvider options={options}>
      <ContactForm />
    </FrontmailProvider>
  );
}

function ContactForm() {
  const { send, status, error } = useSendEmail(null, 'tpl_…'); // null = default service
  const [name, setName] = useState('');
  const [email, setEmail] = useState('');
  const [message, setMessage] = useState('');
  const [token, setToken] = useState<string>();
  const turnstile = useRef<TurnstileWebViewHandle>(null);

  const onSend = async () => {
    await send({ name, email, message }, { turnstileToken: token });
    setToken(undefined);
    turnstile.current?.reset(); // a token works only once
  };

  if (status === 'sent' || status === 'held') return <Text>Thank you! We’ll get back to you soon.</Text>;

  return (
    <View style={{ gap: 12, padding: 16 }}>
      <TextInput placeholder="Name" value={name} onChangeText={setName} />
      <TextInput placeholder="Email" value={email} onChangeText={setEmail} keyboardType="email-address" autoCapitalize="none" />
      <TextInput placeholder="Message" value={message} onChangeText={setMessage} multiline />
      <TurnstileWebView ref={turnstile} onToken={setToken} />
      <Button title="Send" disabled={!token || status === 'sending'} onPress={onSend} />
      {error && <Text>{error.message}</Text>}
    </View>
  );
}

send ne lève jamais d’exception : la promesse se résout avec le résultat ou undefined, et status passe de idle à sending, puis à sent, held ou error. Les nouvelles tentatives après une erreur réseau réutilisent une seule clé d’idempotence : un utilisateur dans le train ne vous enverra jamais deux fois le même message.

5. Testez

Démarrez l’application (npx expo start), remplissez le formulaire et touchez Send. L’e-mail arrive dans votre boîte de réception et dans l’Historique du tableau de bord.

  • origin_not_allowed – Autoriser les applications mobiles est désactivé ou non enregistré.
  • captcha_required – la vérification Turnstile n’est pas encore terminée ; le bouton reste désactivé tant qu’il n’y a pas de jeton.
  • invalid_template_params – un champ obligatoire est vide ou l’e-mail n’est pas valide.

held signifie que le message a été accepté alors que vos crédits étaient épuisés. Il part automatiquement dès que vous rechargez – rien n’est abandonné.

Étapes suivantes

  • Ajoutez limitRate: { id: 'contact', throttle: 30_000 } aux options du provider pour autoriser un message toutes les 30 secondes par appareil.
  • Envoyez une confirmation à l’utilisateur avec le modèle Réponse automatique : merci pour votre message de la galerie.
Tous les tutoriels