Retour au catalogue

Contact Chat Bubbles

Formulaire deguise en conversation chat. Questions en bulles stagger, reponses en bulles utilisateur. Tres engageant.

contactcomplex Both Responsive a11y
playfulboldsaasagencyuniversalcentered
Theme

Créer un formulaire contact sous forme de chat React

Un formulaire contact en bulles de chat React remplace le formulaire classique par une conversation séquentielle : chaque question apparaît en bulle de bot animée avec Framer Motion, l'utilisateur tape une réponse et appuie sur Entrée, et la question suivante glisse en dessous. Le state suit l'index de l'étape en cours et un tableau des réponses collectées.

  • Stack : React 18 + Framer Motion 11 + lucide-react, ~155 lignes, zéro dépendance supplémentaire.
  • Animation : chaque BotBubble entre avec opacity/y/scale via AnimatePresence ; UserBubble reprend le même spring.
  • Supporte les types text, email et textarea par étape, configuré via la prop steps.
  • Accessible : éléments input sémantiques, soumission clavier sur Entrée (Shift+Entrée insère un saut de ligne dans le textarea).
  • Responsive nativement ; la carte est centrée avec un max de 600px et utilise des tokens CSS pour les espacements.

Contact Chat Bubbles transforme un formulaire multi-champs ennuyeux en une conversation à bâtons rompus. Le bot pose une question à la fois dans une bulle à gauche ; la réponse de l'utilisateur flotte à droite. Il supprime la charge visuelle d'un formulaire classique et rend la prise de contact moins intimidante, particulièrement pour les agences créatives et les SaaS ciblant un public non technique.

Anatomie

Le composant est une carte centrée limitée à 600px. Elle se divise en trois zones empilées : une barre d'en-tête avec le nom du bot et un indicateur en ligne, une zone de messages scrollable avec une hauteur minimale de 320px, et une ligne de saisie en bas. Les bulles du bot sont alignées à gauche avec un avatar icône de 32px ; les réponses utilisateur sont des bulles colorées alignées à droite avec un coin arrondi inversé. La zone de saisie disparaît une fois toutes les étapes franchies, remplacée par une bulle de confirmation finale.

Comment ça marche

Le state clé comprend deux valeurs : `currentStep` (un entier) et `answers` (un tableau de chaînes). Quand l'utilisateur soumet, la valeur de l'input est ajoutée aux réponses et currentStep s'incrémente. La zone de messages rend `steps.slice(0, currentStep + 1)`, gardant toutes les questions et réponses précédentes visibles. Chaque BotBubble reçoit une prop `delay` : les étapes déjà répondues se montent avec un délai de 0 (elles se restaurent, elles ne se révèlent pas), tandis que la nouvelle étape courante reçoit 0.3s pour simuler la frappe du bot. AnimatePresence enveloppe la liste avec mode='sync' pour que les nouvelles bulles s'insèrent sans démonter les existantes.

Comment le coder en React

  1. Définir le tableau de steps

    Chaque étape est un objet avec une question, un inputType ('text' | 'email' | 'textarea') et un placeholder. Passe le tableau en prop pour que le script de conversation reste hors du composant, facile à traduire ou tester en A/B.

    const steps = [
      { question: "What's your name?", inputType: "text", placeholder: "Jane Doe" },
      { question: "Your email?",       inputType: "email", placeholder: "[email protected]" },
      { question: "How can we help?",  inputType: "textarea", placeholder: "Tell us…" },
    ];
  2. Gérer le state des étapes

    Garde currentStep et answers dans useState. Le handler de soumission vérifie que inputValue n'est pas vide, l'ajoute aux réponses, vide le champ et incrémente l'étape. Un flag isComplete dérivé de currentStep >= steps.length pilote ce qui s'affiche.

    const [currentStep, setCurrentStep] = useState(0);
    const [answers, setAnswers] = useState<string[]>([]);
    
    const handleSubmit = () => {
      if (!inputValue.trim()) return;
      setAnswers(prev => [...prev, inputValue.trim()]);
      setInputValue("");
      setCurrentStep(prev => prev + 1);
    };
  3. Animer les bulles du bot avec un délai stagger

    Le composant BotBubble accepte une prop delay. Passe `delay={0}` pour les étapes déjà vues (elles s'affichent instantanément au re-render) et `delay={0.3}` pour la question fraîchement révélée. Ce petit détail rend le chat vivant sans indicateur de frappe factice.

    <BotBubble
      text={step.question}
      delay={i === currentStep && answers.length === i ? 0.3 : 0}
    />
  4. Gérer la soumission au clavier

    Pour les inputs text et email, attache `onKeyDown` pour appeler handleSubmit sur Entrée. Pour le textarea, vérifie aussi `!e.shiftKey` pour permettre les sauts de ligne avec Shift+Entrée avant d'envoyer. Appelle `e.preventDefault()` sur Entrée dans le textarea pour éviter la soumission de formulaire ou les sauts de ligne parasites.

    onKeyDown={(e) => {
      if (e.key === "Enter" && !e.shiftKey) {
        e.preventDefault();
        handleSubmit();
      }
    }}

Quand l'utiliser

Ce pattern est pertinent quand le formulaire de contact est un point de conversion principal et que tu veux réduire la friction perçue : demande de devis d'une agence, inscription à un essai SaaS, présentation d'un studio créatif. La nature séquentielle garde l'utilisateur concentré sur une question à la fois. À éviter si le formulaire dépasse six ou sept étapes (ça devient fastidieux), si les champs doivent être modifiables après soumission, ou si tu as besoin d'une validation serveur par étape avec messages d'erreur en temps réel, l'implémentation actuelle collecte les réponses côté client et les envoie toutes en une fois.

Utilisé par

  • Typeform, A popularisé le pattern de formulaire conversationnel une-question-à-la-fois dont ce composant s'inspire.
  • Intercom, Utilise des flows en bulles de chat pour la qualification de leads et le routage du support directement sur les sites clients.
  • Drift, A construit tout son produit autour de formulaires conversationnels intégrés comme widgets de chat sur les landing pages.

FAQ

Comment envoyer les réponses collectées à mon backend ?

Quand isComplete passe à true, déclenche un fetch ou appel axios dans un useEffect qui surveille currentStep. Mappe answers sur ton tableau steps pour reconstruire un payload nommé avant d'envoyer.

Puis-je valider chaque réponse avant de passer à l'étape suivante ?

Oui. Ajoute une fonction `validate` à chaque objet step. Dans handleSubmit, exécute validate(inputValue) et si elle retourne une erreur, écris-la dans un state local et ne pas incrémenter currentStep.

Pourquoi la bulle du bot utilise-t-elle une prop delay plutôt qu'un animation-delay CSS ?

Une prop delay côté JS via Framer Motion s'intègre avec AnimatePresence, ce qui fait que le délai ne se déclenche qu'au montage initial, pas lors des re-renders causés par les changements de state parent. Un animation-delay CSS rejouerait à chaque cycle de rendu.

Fonctionne-t-il sur mobile ?

Le layout est entièrement responsive et la carte occupe son conteneur sur petit écran. Sur iOS, tapez l'input pour ouvrir le clavier ; le bouton Envoyer fait 44px de côté pour respecter les recommandations de taille de cible tactile. Il n'y a pas de dépendance aux pointer events, donc le tactile fonctionne sans traitement spécial.

"use client";

import { useState } from "react";
import { motion, AnimatePresence } from "framer-motion";
import { Send, Bot } from "lucide-react";

interface ChatStep {
  question: string;
  inputType: "text" | "email" | "textarea";
  placeholder: string;
}

interface ContactChatBubblesProps {
  heading?: string;
  subtitle?: string;
  steps?: ChatStep[];
  ctaLabel?: string;
  botName?: string;
}

const EASE = [0.16, 1, 0.3, 1] as const;

Code complet réservé à Pro

Code source intégral, export multi-framework et playground.

Passer en Pro, 9,99€/mois

Avis

Formulaire contact bulles de chat React, Code + Tutoriel