Envoyer un email avec NextJS et SendGrid gratuitement

Vous avez toujours souhaité envoyer des emails avec JavaScript ? Ou plus précisément, comment envoyer des emails avec React voir NextJS ? Alors découvrons ensemble comment utiliser SendGrid ! Il s'agit d'une solution facile et rapide à mettre en place, qui nous permettra de s'assurer que nos emails soient envoyés et le plus important : qu'ils soient bien réceptionnés.

 

Etape 1 : Créer un projet NextJS

Cette étape est parfaitement facultative. Si vous disposez déjà d'un projet NextJS ainsi que d'un formulaire parfaitement fonctionnel qui réceptionne bien les données de vos utilisateurs (formulaire de contact), vous pouvez passer à l'étape 4.

Pour comprendre le mode de fonctionnement lors que nous envoyons un email, nous vous proposons de partir d'un projet NextJS créé à partir de zéro.

Dans ce projet, nous allons ajouter une page de contact qui montrera un formulaire, dans lequel nous pourrons renseigner notre nom, prénom, adresse email et le contenu de notre message. Vous pourrez ensuite personnaliser ce projet en réutilisant la même méthode que nous aurons vu ensemble pour envoyer toutes sortes d'emails, allant des notifications de sécurité lorsqu'un utilisateur se connecte à l'envoi d'un code de sécurité pour finaliser et confirmer son inscription.

Afin de créer un projet avec NextJS, commençons par utiliser cette commande :

npx create-next-app mon-formulaire-de-contact

Cette commande va créer pour nous un projet qui s'appellera "Mon formulaire de contact" avec toutes les dépendances qu'il nous faut.

Nous en ajouterons bien évidemment dans la suite de ce mini-cours.

 

Etape 2 : Créer une page de contact

Pour ajouter une nouvelle page dans un projet réalisé avec NextJS, il suffit d'ajouter un fichier JavaScript dans le dossier pages.

Comme nous souhaitons ajouter une page de contact, nous voulons que cette page soit accessible depuis /contact. Nous allons donc créer la page contact.js dans le dossier pages.

Vous pouvez aussi créer un dossier contact dans le dossier pages, et y créer un fichier index.js. Cette méthode est valide si vous le désirez.

Ajoutons maintenant le JSX qui nous permettra d'afficher un joli formulaire de contact. Voici ce que nous aurons dans le code final de la page contact.js pour le moment :

 

Et pour le CSS (dans le fichier globals.css) :

 

 

Etape 3 : Initialiser React Hook Form

Nous venons d'ajouter une nouvelle page et d'y mettre un formulaire de contact : nous pouvons être fiers du chemin parcouru jusqu'à maintenant.

Le problème est le suivant : notre formulaire ne fonctionne pas. Et ça, c'est plutôt embêtant.

Ce que nous voulons dire par là, c'est que le formulaire ne renvoi pas des données à NextJS - ou plutôt, nous ne récupérons pas les données envoyées.

Pour ce faire, nous allons utiliser la librairie react-hook-form. Cette superbe librairie est l'une des meilleures manières de gérer les formulaires React et NextJS en ce moment. Il existe évidemment de nombreuses autres possibilités, vous êtes libres d'utiliser la méthode qui vous convient le mieux.

Commençons par installer React Hook Form :

npm install react-hook-form

Allons maintenant sur notre page contact.js et ajoutons cette nouvelle dépendance tout en haut du fichier :

import { useForm } from 'react-hook-form';

Nous pouvons maintenant commencer à récupérer les données de notre formulaire !

 

Etape 4 : Afficher les données reçues

Pour afficher les données que nous recevons, nous devons commencer par mettre en place React Hook Form pour lui dire "Je te donne le contrôle du champ de formulaire prénom", puis "nom", "adresse email", et pour conclure "contenu".

Pour réaliser ce genre de chose, nous allons devoir utiliser plusieurs variables qui nous sont retournées par React Hook Form. Voici comment nous allons faire.

// Librairie
import { useForm } from 'react-hook-form';

export default function Contact() {
    // Variables
    const { register, handleSubmit, formState: { errors } } = useForm();

    // JSX
    return ...
}

Toutes ces variables sont récupérées depuis useForm() et ont leur utilitée :

  • register - permet d'enregistrer un champ de formulaire auprès de React Hook Form
  • handleSubmit - permet de récupérer toutes les données du formulaire et de les envoyer à une méthode très spécifique
  • errors - permet de savoir lorsqu'un champ de formulaire est invalide

Bien entendu nous ne sommes pas là pour faire un mini-cours sur React Hook Form, mais voici quelques éléments de base qui peuvent être intéressants lorsque nous ne connaissons pas bien cette librairie.

Nous avons donc les variables nécessaires au bon fonctionnement de React Hook Form, ajoutons maintenant nos champs de formulaire ! Voici ce que nous avons pour le moment sur le champ prenom :

<input className="input" placeholder="Adresse Email" />

Voici ce que nous allons avoir grâce à React Hook Form :

<input
    className="input"
    placeholder="Adresse Email"
    {...register('prenom', { required: true }) }
/>

Ici, nous enregistrons le champ de formulaire avec l'identifiant prenom pour React Hook Form et nous ajoutons une règle de validation pour que le champ soit requis à chaque fois. Voici ce que nous aurons pour l'ensemble de notre formulaire :

 

Je vous propose maintenant de récupérer toutes ces informations lorsque notre formulaire est envoyé.

Voici une bonne nouvelle : c'est très simple à mettre en place grâce à React Hook Form !

Nous allons ajouter l'événement onSubmit sur la balise <form> de notre formulaire, et nous allons lui dire d'appeler la méthode handleSubmit qui est proposée par React Hook Form.

<form onSubmit={handleSubmit()}>

N'oubliez pas que cette méthode permet de récupérer toutes les informations du formulaire et de les envoyer à une méthode très spécifique : notre propre méthode ! Disons que nous aurons une méthode onSubmitHandler, précisons-le lui dans handleSubmit de ce pas.

<form onSubmit={handleSubmit(onSubmitHandler)}>

Il ne nous reste plus qu'à afficher nos informations en créant la méthode onSubmitHandler dans notre page de contact.

// Librairie
import { useForm } from 'react-hook-form';

export default function Contact() {
    // Variables
    const { register, handleSubmit, formState: { errors } } = useForm();

    // Méthode
    const onSubmitHandler = data => {
        console.log(data);
    }

    // JSX
    return ...
}

Ajoutons des informations, envoyons le formulaire... Nos données sont bien affichées dans la console ! Merveilleux.

Vous connaissez peut-être notre réputation : nous allons toujours en profondeur, y compris quand c'est seulement du contenu proposé gratuitement. Nous allons donc vous montrer comment afficher un message à vos utilisateurs lorsqu'ils ne remplissent pas toutes les informations.

Concrétement, lorsqu'un champ du formulaire est vide, React Hook Form met le curseur dessus lorsque l'utilisateur veut envoyer le formulaire. Mais afficher un petit message lui demandant de remplir le champ, c'est quand même beaucoup mieux.

Voici comment nous pouvons faire ce genre de chose (n'oubliez pas notre variable errors que nous avions ajouté) :

 

Rien qu'avec ce code, lorsque notre utilisateur ne remplira pas son prénom, un petit message s'affichera et disparaîtra tout seul comme par magie lorsqu'il aura ou n'aura pas ajouté une valeur. Magique n'est-ce pas ?

 

Etape 5 : Créer un compte sur SendGrid

Nous disposons maintenant d'un formulaire de contact qui est parfaitement fonctionnel, il ne nous reste plus qu'à envoyer ces informations pour envoyer un email. Et pour ça, nous avons évidemment besoin de SendGrid dans ce mini-cours.

Vous pouvez donc créer un compte sur SendGrid en allant ici.

La création d'un compte est gratuite, vous ne payerez que si vous souhaitez envoyer plus de 100 emails par jour (ce qui est extrêmement généreux).

Dashboard

 

Etape 6 : Créer une template

Nous souhaitons maintenant aller sur la catégorie Email API et enfin sur Dynamic Templates.

Les templates dynamiques sont des emails que vous pourrez personnaliser avec des informations très précises, ainsi nous allons paramétrer le design de notre email et nous le remplirons avec les informations renseignées par notre utilisateur au moment de l'envoi du formulaire : SendGrid is magic! (SendGrid c'est magique ! - Oui, on pense à vous, qui n'aimez pas l'anglais)

Nous allons cliquer sur Create a Dynamic Template - le gros bouton bleu sur la page - et nous allons donner un nom à notre template. Par exemple contact.

Vous devriez maintenant voir la template Contact sur votre page, cliquez dessus et cliquez sur Add Version.

Nous pouvons maintenant choisir entre deux possibilités :

  • Ajouter un design (Your email design) ;
  • Utiliser une template SendGrid (SendGrid Email Designs).

Vous pouvez utiliser celle que vous préférez. Sachez que nous vous recommandons d'utiliser une template SendGrid pour commencer. Voici celle que nous allons choisir :

Nous vous recommandons d'utiliser le mode designer et pas le mode code. Il est plus simple de naviguer dans la template de cette manière, vous pourrez toujours modifier le HTML lorsque nous en aurons besoin.

Essayez donc de personnaliser la template selon vos goûts. Si vous avez du mal, vous pouvez toujours consulter notre vidéo YouTube à ce sujet.

Voici ce que nous avons fait :

Nous allons maintenant rendre la template dynamique. L'objectif est d'avoir un message personnalisé avec les informations renseignées par chaque utilisateur, nous nous attendons donc à avoir :

  • prenom ;
  • nom ;
  • contenu ;
  • email.

Sans majuscules et sans aucun caractère spécial.

Ce qui est très simple avec SendGrid, c'est que pour ajouter des informations dynamiques, il suffit d'aller dans le HTML et de remplacer le texte par :

{{ prenom }}

Pour afficher le prénom dynamiquement. Et si vous souhaitez autoriser le HTML, il suffit d'ajouter un autre couple d'accolades tout autour.

{{{ contenu }}}

Vous l'aurez donc compris, essayez de faire en sorte d'ajouter toutes les informations dynamiques dans votre template afin que chaque message envoyé soit unique.

Vous pouvez tester vos variables avec le mode prévisualisation (cliquez sur Preview). Vous pourrez cliquer ensuite sur le bouton "Add Test Data" et y écrire du JSON. Voici un exemple de JSON pour tester votre template sur SendGrid avec les variables.

{
 "contenu": "Je souhaite créer un site pour ma nouvelle entreprise SpaceX. Serait-il possible de me renseigner sur le Programme Rocket ? J'en ai entendu beaucoup de bien.",
 "prenom": "Elon",
 "nom": "Musk",
 "email": "[email protected]"
}

 

Etape 7 : Créer une API sur NextJS

Le formulaire de contact est fonctionnel, notre template sur SendGrid aussi, nous devons juste lui envoyer des informations pour qu'il puisse nous transmettre l'email.

Pour ce faire, nous allons créer une API grâce au système proposé par NextJS.

Il suffit de créer un dossier api dans le dossier pages et d'y ajouter un fichier, par exemple le fichier contact.js. Tout ce qui est écrit dans ce fichier restera caché des yeux de notre utilisateur, nous pouvons y ajouter nos identifiants de connexion à SendGrid par exemple, l'ensemble du code sera éxecuté sur le serveur, par sur le navigateur. Il s'agit d'un des gros avantages proposés avec NextJS. Voici donc ce que nous aurons dans notre API :

  • Une vérification de la méthode utilisée ;
  • Une vérification de la syntaxe de l'adresse email ;
  • Une requête pour envoyer les informations sur SendGrid ;
  • Une vérification pour retourner un succès ou un échec d'envoi.

Vous pouvez bien entendu créer tout ceci par vous-même, c'est d'ailleurs ce que nous aimerions que vous fassiez : il n'y a pas mieux pour apprendre que d'essayer. Voici tout de même ce que nous avons :

// Librairie
import sgMail from "@sendgrid/mail";

export default function handler(req, res) {
	if (req.method !== "POST") {
		res.status(405).json({ message: "INVALID_METHOD" });
		return;
	}

	// Variables
	const { prenom, nom, email, contenu } = req.body;

	if (!prenom || !nom || !email || !contenu) {
		res.status(400).json({ message: "INVALID_PARAMETER" });
		return;
	}

	// Syntaxe adresse email
	const pattern =
		/^(([^<>()\[\]\\.,;:\[email protected]"]+(\.[^<>()\[\]\\.,;:\[email protected]"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
	if (!pattern.test(email)) {
		res.status(400).send({
			message: "EMAIL_SYNTAX_INCORRECT",
		});
		return;
	}

	// Transformer les retours à la ligne pour le HTML
	const message = contenu
		.replace(/\n/g, "<br>")
		.replace(/\r/g, "<br>")
		.replace(/\t/g, "<br>")
		.replace(/<(?!br\s*\/?)[^>]+>/g, ""); // supprime tout le html en autorisant uniquement les balises <br>

	// Donner la clé API
	sgMail.setApiKey(process.env.KEY_SENDGRID);

	// Création du message
	const sendGridMail = {
		to: "[email protected]",
		from: "[email protected]",
		templateId: "d-98ac3ceded9745448d3f4522a0f3eb26",
		dynamic_template_data: {
			prenom: prenom,
			nom: nom,
			email: email,
			contenu: message,
		},
	};
	// SendGrid
	(async () => {
		try {
			await sgMail.send(sendGridMail);
			res.status(200).json({
				message: "EMAIL_SENDED_SUCCESSFULLY",
			});
		} catch {
			res.status(500).json({
				message: "ERROR_WITH_SENDGRID",
			});
			return;
		}
	})();
}

Comme vous pouvez le voir, rien de bien nouveau si vous connaissez déjà NextJS. Si ce n'est pas le cas, un petit tour sur notre cours complet pour apprendre NextJS est fortement conseillé. Nous avons d'ailleurs utilisé une variable d'environnement (process.env.KEY_SENGRID). Pour ajouter une variable d'environnement, allez dans le fichier next.config.js et ajoutez ceci :

module.exports = {
	reactStrictMode: true,
	env: {
		KEY_SENDGRID:
			"LA CLE API",
	},
};

Votre clé pour vous permettre d'utiliser des requêtes vers l'API de SendGrid se trouve dans votre compte, sur settings et api keys. Il vous suffit d'en créer une nouvelle avec tous les droits.

 

Etape 8 : Appeler l'API sur le client

Notre API fonctionne, il ne reste plus qu'à l'utiliser !

Afin de l'appeler, nous avons besoin d'un déclencheur, un moment où nous savons que notre formulaire est envoyé serait parfait ! En y réfléchissant un peu, nous avons justement ce déclencheur : la méthode onSubmitHandler.

Voici ce que nous pourrions faire :

const onSubmitHandler = async (data) => {
    const response = await fetch("/api/contact", {
        method: "POST",
        headers: {
            "Content-Type": "application/json",
        },
        body: JSON.stringify(data),
    });

    const result = await response.json();
    
    if (!response.ok) {
        console.log("error");
    } else {
        console.log("ok");
    }
};

N'oublions pas d'ajouter un petit message pendant l'envoi pour que notre utilisateur ne patiente pas en pendant que le formulaire ne fonctionne pas. Pour ceci, nous pouvons utiliser un state avec useState. Appelons-le isLoading.

// Librairies
// ...
import { useState } from 'react';

export default function Contact() {
    // Variables
    // ...

    // State
    const [isLoading, setIsLoading] = useState(false);

    // ...
}

Modifions le state lorsque nous utilisons l'API que nous avons conçu, et ajoutons une condition pour éviter l'envoi de plusieurs requêtes en même temps. :

const onSubmitHandler = async (data) => {
    if (!isLoading) {
        setIsLoading(true);

        const response = await fetch("/api/contact", {
            method: "POST",
            headers: {
                "Content-Type": "application/json",
            },
            body: JSON.stringify(data),
        });

        const result = await response.json();

        setIsLoading(false);

        if (!response.ok) {
            console.log("error");
        } else {
            console.log("ok");
        }
    }
};

Si nous ne faisons aucune vérification, notre utilisateur qui aurait cliqué 15 fois sur le bouton "Envoyer" nous aurait envoyé 15 fois le même email ! Pensez donc toujours à vérifier si le formulaire n'est pas déjà en cours d'envoi.

Pour terminer nous n'avons plus qu'à vider le formulaire et à afficher un petit message une fois que l'email est correctement reçu par SendGrid. Voici ce que nous allons faire pour vider le formulaire :

// Variables
const {
    register,
    handleSubmit,
    reset, // nous ajoutons la méthode reset
    formState: { errors },
} = useForm();

// State
const [isLoading, setIsLoading] = useState(false);

// Méthode
const onSubmitHandler = async (data) => {
    if (!isLoading) {
        setIsLoading(true);

        const response = await fetch("/api/contact", {
            method: "POST",
            headers: {
                "Content-Type": "application/json",
            },
            body: JSON.stringify(data),
        });

        const result = await response.json();

        setIsLoading(false);

        if (!response.ok) {
            console.log("error");
        } else {
            console.log("ok");
            reset(); // ici
        }
    }
};

Très facile non ? Nous avons juste à ajouter le message de succès et tout sera parfaitement fonctionnel. Nous vous recommandons d'utiliser un nouveau state isSended.

// Librairies
// ...
import { useState } from 'react';

export default function Contact() {
    // Variables
    // ...

    // States
    const [isLoading, setIsLoading] = useState(false);
    const [isSended, setIsSended] = useState(true);

    // ...
}

Voici ce que nous avons maintenant dans onSubmitHandler :

const onSubmitHandler = async (data) => {
    if (!isLoading) {
        setIsLoading(true);

        const response = await fetch("/api/contact", {
            method: "POST",
            headers: {
                "Content-Type": "application/json",
            },
            body: JSON.stringify(data),
        });

        const result = await response.json();

        setIsLoading(false);

        if (!response.ok) {
            console.log("error");
        } else {
            console.log("ok");
            reset();
            setIsSended(true);
        }
    }
};

Il ne reste plus qu'à afficher le message de succès au-dessus de notre formulaire lorsque le state isSended est modifié et vaut true.

// Le reste du code

<form
    style={{ width: "500px", margin: "auto" }}
    onSubmit={handleSubmit(onSubmitHandler)}
>
    {isSended && (
        <p>
            Votre message a bien été envoyé avec
            succès nous vous répondrons rapidement.
        </p>
    )}
    <div
        style={{
            backgroundColor: "#f5f5f5",
            padding: "30px",
            borderRadius: "5px",
            textAlign: "left",
        }}
    >

    // La fin du code

Et voilà le travail ! Nous avons maintenant un formulaire de contact très bien pensé, parfaitement utilisable et fonctionnant grâce à SendGrid afin que tous les emails soient bien envoyés jusqu'à la destination.

Vous n'avez plus qu'à réutiliser ce même modèle pour envoyer d'autres emails à d'autres occasions si vous le désirez, il vous suffit de créer une template - qui soit dynamique ou non - puis d'envoyer une requête à SendGrid.

Si vous souhaitez en savoir plus sur NextJS, nous avons une formation complète dédiée à ce sujet. N'hésitez pas non plus à partager cet article à votre entourage sur vos réseaux-sociaux. Merci !

N'hésitez pas à retrouver le code source du projet en téléchargement ici.