<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Olufeyimi Samuel</title>
    <description>The latest articles on DEV Community by Olufeyimi Samuel (@donsmog).</description>
    <link>https://dev.to/donsmog</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1064604%2F928afbe4-c545-4f35-8c85-6b0a5d72e332.jpeg</url>
      <title>DEV Community: Olufeyimi Samuel</title>
      <link>https://dev.to/donsmog</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/donsmog"/>
    <language>en</language>
    <item>
      <title>Data Fetching with React Query and TypeScript using the Movie Database Api</title>
      <dc:creator>Olufeyimi Samuel</dc:creator>
      <pubDate>Tue, 09 May 2023 14:00:59 +0000</pubDate>
      <link>https://dev.to/donsmog/data-fetching-with-react-query-and-typescript-using-the-movie-database-api-4ddg</link>
      <guid>https://dev.to/donsmog/data-fetching-with-react-query-and-typescript-using-the-movie-database-api-4ddg</guid>
      <description>&lt;p&gt;A few years back when I started learning ReactJS, I was introduced to data fetching using React Hooks, specifically the useState and useEffect hooks. But every time the page paints/loads, the data is been re-fetched. This suffice for a while until I found a better way of handling data fetching using &lt;strong&gt;React Query&lt;/strong&gt;. Data fetching with React Query was a game changer for me.&lt;/p&gt;

&lt;p&gt;This article is targeted towards beginners like I was few years ago. To illustrate the usage of React Query better, we will be creating a single page movie page using the movie database api.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Please note:&lt;/strong&gt; this article won’t cover how to setup an account on the movie database (TMDB). If needed, I will write on that in another article. For this project, I already have my api key from TMDB and that will be used in this example. To learn more about TMDB, kindly &lt;a href="https://themoviedb.org/" rel="noopener noreferrer"&gt;check out their official website&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So let’s jump in…&lt;br&gt;
To begin, let’s create a new React Project using Vite.&lt;/p&gt;

&lt;p&gt;Open your VSCode or any other code editor, open the terminal and type in &lt;code&gt;yarn create vite&lt;/code&gt; or &lt;code&gt;npm create vite@latest&lt;/code&gt; then follow the prompts. I will be using React with TypeScript in this example.&lt;br&gt;
You can checkout &lt;a href="https://vitejs.dev/guide/" rel="noopener noreferrer"&gt;the official documentation page&lt;/a&gt; for more information on Vite. &lt;/p&gt;

&lt;p&gt;Next, let’s install some dependencies. In your terminal, type in &lt;code&gt;yarn add axios @tanstack/react-query&lt;/code&gt;. Then run &lt;code&gt;yarn dev&lt;/code&gt; to start the project.&lt;/p&gt;

&lt;p&gt;As stated above, I’ve setup my TMDB account. For this illustration, we will be using &lt;a href="https://developer.themoviedb.org/reference/discover-movie" rel="noopener noreferrer"&gt;the discover movies api&lt;/a&gt;. This api have about 30 params for filtering. However, we will only use ‘sort_by’ and ‘page’ params in addition to the compulsory ‘api_key’. Our endpoint will look like this &lt;code&gt;https://api.themoviedb.org/3/discover/movie?sort_by=popularity.desc&amp;amp;api_key=${my_api_key}&amp;amp;page=${page}&lt;/code&gt;&lt;br&gt;
The page will be controlled to allow for fetching of the next page.&lt;/p&gt;

&lt;p&gt;Just to speed things up, I’ve implemented a simple movie page using the regular useState and useEffect. I’m displaying the movie title, movie thumbnail, movie rating and movie description which is seen on hover of each movie card.&lt;br&gt;
The project structure looks something like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe1gelb45t5umkpsr51ru.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe1gelb45t5umkpsr51ru.png" alt="Project directory" width="326" height="591"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As seen in the directory above, there are 4 major files in the src folder, App.tsx, main.tsx, movie.tsx and index.css. I modified App.tsx and index.css and created a new movie.tsx. The main.tsx is as it were for now.&lt;/p&gt;

&lt;p&gt;Using our useState and useEffect, we can fetch the data within the App.tsx component as seen below.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { useEffect, useState } from "react";
import Movie from "./movie";

export interface Movies {
  id?: number;
  title: string;
  poster_path: string;
  overview: string;
  vote_average: number;
}

const my_api_key = import.meta.env.VITE_API_KEY;

function App() {
  const [loading, setLoading] = useState&amp;lt;boolean&amp;gt;(false);
  const [movies, setMovies] = useState&amp;lt;Movies[]&amp;gt;([]);
  const [page, setPage] = useState&amp;lt;number&amp;gt;(1);

  const FeaturedApi = `https://api.themoviedb.org/3/discover/movie?sort_by=popularity.desc&amp;amp;api_key=${my_api_key}&amp;amp;page=${page}`;

  const getMovies = (API: string) =&amp;gt; {
    setLoading(true);
    fetch(API)
      .then((res) =&amp;gt; res.json())
      .then((data) =&amp;gt; {
        setMovies(data.results);
        setLoading(false);
      });
  };

  useEffect(() =&amp;gt; {
    getMovies(FeaturedApi);
  }, [FeaturedApi]);

  return (
    &amp;lt;div className="movie_app"&amp;gt;
      &amp;lt;h1 className="header"&amp;gt;
        Simple Movie App with React Query and TypeScript
      &amp;lt;/h1&amp;gt;

      &amp;lt;div className="movie-container"&amp;gt;
        {loading ? (
          &amp;lt;h1 className="loading"&amp;gt;Loading...&amp;lt;/h1&amp;gt;
        ) : (
          &amp;lt;&amp;gt;
            {movies.length &amp;gt; 0 &amp;amp;&amp;amp;
              movies.map((movie) =&amp;gt; &amp;lt;Movie key={movie.id} {...movie} /&amp;gt;)}
          &amp;lt;/&amp;gt;
        )}
      &amp;lt;/div&amp;gt;

      &amp;lt;div className="pagination"&amp;gt;
        &amp;lt;button
          onClick={() =&amp;gt; setPage((prev) =&amp;gt; prev - 1)}
          disabled={page === 1 ? true : false}
        &amp;gt;
          Prev
        &amp;lt;/button&amp;gt;
        &amp;lt;button
          onClick={() =&amp;gt; setPage((prev) =&amp;gt; prev + 1)}
          disabled={page === 100 ? true : false}
        &amp;gt;
          Next
        &amp;lt;/button&amp;gt;
      &amp;lt;/div&amp;gt;
    &amp;lt;/div&amp;gt;
  );
}

export default App;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the snippet above, the constant my_api_key is gotten from TMDB.&lt;br&gt;
The movie.tsx component looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { Movies } from "./App";

const Movie = ({ title, poster_path, overview, vote_average }: Movies) =&amp;gt; {
  const ImagesApi = "https://image.tmdb.org/t/p/w1280";
  const setVoteClass = (vote: number) =&amp;gt; {
    if (vote &amp;gt;= 8) {
      return "green";
    } else if (vote &amp;gt;= 6) {
      return "orange";
    } else {
      return "red";
    }
  };

  return (
    &amp;lt;div className="movie"&amp;gt;
      &amp;lt;img
        src={
          poster_path
            ? ImagesApi + poster_path
            : "https://indianfolk.com/wp-content/uploads/2018/10/Movie.jpg"
        }
        alt={title}
      /&amp;gt;
      &amp;lt;div className="movie-info"&amp;gt;
        &amp;lt;h3&amp;gt;{title}&amp;lt;/h3&amp;gt;
        &amp;lt;span className={`tag ${setVoteClass(vote_average)}`}&amp;gt;
          {vote_average}
        &amp;lt;/span&amp;gt;
      &amp;lt;/div&amp;gt;
      &amp;lt;div className="movie-over"&amp;gt;
        &amp;lt;h2&amp;gt;Overview:&amp;lt;/h2&amp;gt;
        &amp;lt;p&amp;gt;{overview}&amp;lt;/p&amp;gt;
      &amp;lt;/div&amp;gt;
    &amp;lt;/div&amp;gt;
  );
};

export default Movie;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What we've simply done is declared a getMovies function that takes in 1 parameter which is the api. We then use the regular fetch function to call the api. Once there's a response, we convert to JSON and then store in the movies state. At the beginning of the call, we change the loading state to true and at the end of the process, we change the loading state back to false.&lt;/p&gt;

&lt;p&gt;Within the body of the component, we display a loading message while data fetching is ongoing then we map out the new updated movies array once the data fetching process is completed.&lt;/p&gt;

&lt;p&gt;We also have a pagination part with 2 buttons for increasing and decreasing the page state.&lt;/p&gt;

&lt;p&gt;This implementation works perfectly fine. However, it is data consuming. Every time we increment or decrement pages, we are calling the endpoint over and over again. Also, we get a blink "Loading..." message every time the page changes. &lt;/p&gt;

&lt;p&gt;What if we could cache or temporarily store the previously fetched data and only update it in the background when there's a new update? This and many more is what React Query allows us to do.&lt;/p&gt;

&lt;p&gt;What exactly is React Query?&lt;br&gt;
React Query is a JavaScript library that allows you fetch, cache, synchronize and update server state in your web application. In simple terms, that means with RQ, you can fetch data, temporarily store the data, update the data in the background and synchronize your display with the most updated data. You can &lt;a href="https://tanstack.com/query/latest/docs/react/overview" rel="noopener noreferrer"&gt;read more here&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;The fun part of RQ is that it is so easy to include in your codebase. We will cover that part right away.&lt;/p&gt;

&lt;p&gt;In our main.tsx, we will import 2 functions from react-query and initialize it. We do:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We then create a client by doing&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const queryClient = new QueryClient();
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then we wrap our root App component with the QueryClientProvider like this&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render(
  &amp;lt;React.StrictMode&amp;gt;
    &amp;lt;QueryClientProvider client={queryClient}&amp;gt;
      &amp;lt;App /&amp;gt;
    &amp;lt;/QueryClientProvider&amp;gt;
  &amp;lt;/React.StrictMode&amp;gt;
);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, we go back into our App.tsx and import 2 things&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import axios from "axios";
import { useQuery } from "@tanstack/react-query"; 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We then modify our getMovies function to be&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const getMovies = async () =&amp;gt; {
    return await axios.get(FeaturedApi).then((res) =&amp;gt; res.data.results);
  };
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can now make use of useQuery as such&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const { isLoading, data } = useQuery&amp;lt;Movies[]&amp;gt;({
    queryKey: ["movies", page],
    queryFn: getMovies,
  });
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What this will do is cache each page with a unique key, in this case as movies-(page). E.g. When page is 1, the unique key is movies-1. This way, you won't have to refetch page 1 as its already cached. Its important to note that the default cache time is 5mins, meaning your data can persist for 5 minutes. However,this option can be changed.&lt;/p&gt;

&lt;p&gt;Going forward, we can remove the loading state and the movie state in the previous App.tsx component.&lt;/p&gt;

&lt;p&gt;In the jsx, we update the map section to be&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{isLoading ? (
          &amp;lt;h1 className="loading"&amp;gt;Loading...&amp;lt;/h1&amp;gt;
        ) : (
          &amp;lt;&amp;gt;
            {(data ?? []).length &amp;gt; 0 &amp;amp;&amp;amp;
              (data ?? []).map((movie) =&amp;gt; &amp;lt;Movie key={movie.id} {...movie} /&amp;gt;)}
          &amp;lt;/&amp;gt;
        )}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And there you have it. You can view the demo of what we've done &lt;a href="https://codesandbox.io/p/sandbox/react-query-demo-cwvph7" rel="noopener noreferrer"&gt;here&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Since I found React Query, it has been my go to data fetching package. There are many other things we can do with React Query, but we won't touch on those in this article.&lt;/p&gt;

&lt;p&gt;If this was helpful, do leave a like and if there's any part not clear, do drop a comment and I will be so glad to help.&lt;/p&gt;

&lt;p&gt;Cheers!!!&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>reactquery</category>
      <category>typescript</category>
      <category>react</category>
    </item>
    <item>
      <title>How to use EmailJS for a Contact Us page</title>
      <dc:creator>Olufeyimi Samuel</dc:creator>
      <pubDate>Sun, 07 May 2023 20:16:46 +0000</pubDate>
      <link>https://dev.to/donsmog/how-to-use-emailjs-for-a-contact-us-page-287p</link>
      <guid>https://dev.to/donsmog/how-to-use-emailjs-for-a-contact-us-page-287p</guid>
      <description>&lt;p&gt;I remember some time ago while working on a side project. I was to implement a simple contact us page as part of the project. Initially, I used Google Firebase to store the responses. Then, I found &lt;strong&gt;EmailJS&lt;/strong&gt; and it was so simple to use and very efficient.&lt;/p&gt;

&lt;p&gt;In this article, you will learn how to set up a EmailJS account and integrate it into your ReactJS/NextJS application.&lt;/p&gt;

&lt;p&gt;First, let’s start by creating a new React Project using Vite.&lt;br&gt;
Open your VSCode or any other code editor, open the terminal and type in:&lt;br&gt;
&lt;code&gt;yarn create vite&lt;/code&gt; or &lt;code&gt;npm create vite@latest&lt;/code&gt; then follow the prompts. I will be using React with Typescript in this example.&lt;/p&gt;

&lt;p&gt;You can checkout &lt;a href="https://vitejs.dev/guide/" rel="noopener noreferrer"&gt;the official vite website&lt;/a&gt; for more information on Vite.&lt;/p&gt;

&lt;p&gt;Next, let’s install some dependencies. In your terminal, type in &lt;code&gt;yarn add @emailjs/browser react-hook-form&lt;/code&gt;. Then run &lt;code&gt;yarn dev&lt;/code&gt; to start the project.&lt;br&gt;
You can get more info on &lt;a href="https://react-hook-form.com/get-started/" rel="noopener noreferrer"&gt;react-hook-form here&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I will delete some default files in the directory and change some styles as seen below.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft4beoelzcbi8nj759btr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft4beoelzcbi8nj759btr.png" alt="Project directory"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Next, in my App.tsx, I will put up a basic contact us form.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

import styles from "./app.module.css";
import { useForm } from "react-hook-form";
import { useState } from "react";

interface IFormInput {
  firstName: string;
  lastName: string;
  email: string;
  phone: string;
  message: string;
}
function App() {
  const [loading, setLoading] = useState(false);

  const {
    register,
    handleSubmit,
    reset,
    formState: { errors },
  } = useForm&amp;lt;IFormInput&amp;gt;();
  const onSubmit = async (data: IFormInput) =&amp;gt; {
    console.log(data);
  };

  return (
    &amp;lt;div className={styles.contact_form}&amp;gt;
      &amp;lt;h1&amp;gt;Contact Form&amp;lt;/h1&amp;gt;
      &amp;lt;p&amp;gt;Our friendly team would love to hear from you.&amp;lt;/p&amp;gt;

      &amp;lt;form className={styles.form} onSubmit={handleSubmit(onSubmit)}&amp;gt;
        &amp;lt;div className={styles.group}&amp;gt;
          &amp;lt;div className={styles.input_group}&amp;gt;
            &amp;lt;label htmlFor="firstName"&amp;gt;First Name&amp;lt;/label&amp;gt;
            &amp;lt;input
              type="text"
              {...register("firstName", { required: true })}
              placeholder="Enter your first name"
            /&amp;gt;
            {errors.firstName &amp;amp;&amp;amp; (
              &amp;lt;span className={styles.error}&amp;gt;This field is required&amp;lt;/span&amp;gt;
            )}
          &amp;lt;/div&amp;gt;
          &amp;lt;div className={styles.input_group}&amp;gt;
            &amp;lt;label htmlFor="lastName"&amp;gt;Last Name&amp;lt;/label&amp;gt;
            &amp;lt;input
              type="text"
              {...register("lastName", { required: true })}
              placeholder="Enter your last name"
            /&amp;gt;
            {errors.lastName &amp;amp;&amp;amp; (
              &amp;lt;span className={styles.error}&amp;gt;This field is required&amp;lt;/span&amp;gt;
            )}
          &amp;lt;/div&amp;gt;
        &amp;lt;/div&amp;gt;
        &amp;lt;div className={styles.input_group}&amp;gt;
          &amp;lt;label htmlFor="email"&amp;gt;Email Address&amp;lt;/label&amp;gt;
          &amp;lt;input
            type="email"
            {...register("email", { required: true })}
            placeholder="Enter your email address"
          /&amp;gt;
          {errors.email &amp;amp;&amp;amp; (
            &amp;lt;span className={styles.error}&amp;gt;This field is required&amp;lt;/span&amp;gt;
          )}
        &amp;lt;/div&amp;gt;
        &amp;lt;div className={styles.input_group}&amp;gt;
          &amp;lt;label htmlFor="phone"&amp;gt;Phone Number&amp;lt;/label&amp;gt;
          &amp;lt;input
            type="tel"
            {...register("phone", { required: true })}
            placeholder="+2348123456789"
          /&amp;gt;
          {errors.phone &amp;amp;&amp;amp; (
            &amp;lt;span className={styles.error}&amp;gt;This field is required&amp;lt;/span&amp;gt;
          )}
        &amp;lt;/div&amp;gt;
        &amp;lt;div className={styles.input_group}&amp;gt;
          &amp;lt;label htmlFor="message"&amp;gt;Message&amp;lt;/label&amp;gt;
          &amp;lt;textarea
            {...register("message", { required: true })}
            placeholder="Leave us a message..."
          /&amp;gt;
          {errors.message &amp;amp;&amp;amp; (
            &amp;lt;span className={styles.error}&amp;gt;This field is required&amp;lt;/span&amp;gt;
          )}
        &amp;lt;/div&amp;gt;

        &amp;lt;button disabled={loading} className={styles.button} type="submit"&amp;gt;
          {loading ? "Sending..." : "Send Message"}
        &amp;lt;/button&amp;gt;
      &amp;lt;/form&amp;gt;
    &amp;lt;/div&amp;gt;
  );
}

export default App;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;In the code snippet above, I’m using CSS module for my component alongside react-hook-form for my form validation. I declared a loading state set to false by default.&lt;/p&gt;

&lt;p&gt;Next, I want to go to &lt;a href="https://www.emailjs.com/" rel="noopener noreferrer"&gt;the official EmailJS website&lt;/a&gt; and signup for a free account. The signup is straightforward.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Faq0zzvj9hgt9j2rmb7iw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Faq0zzvj9hgt9j2rmb7iw.png" alt="EmailJS website"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Next, I will sign in to my account and setup a new email service. For this example, we will use only Gmail. On your dashboard, select &lt;strong&gt;Add New Service&lt;/strong&gt; and select &lt;strong&gt;Gmail&lt;/strong&gt;. Then select &lt;strong&gt;Connect Service&lt;/strong&gt;. This will take you to your Gmail account to link together with Emailjs. Once done, select &lt;strong&gt;Create Service&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4prun37s7rk684wbvpyi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4prun37s7rk684wbvpyi.png" alt="Add New Service"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp6r2p9pgiychaqvq0k0s.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp6r2p9pgiychaqvq0k0s.png" alt="Gmail Service"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkqdzf9j87csnygktby38.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkqdzf9j87csnygktby38.png" alt="Connect and Create Service"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Next, we need to create an Email Template. On your dashboard, select &lt;strong&gt;Email Templates&lt;/strong&gt; and select &lt;strong&gt;Create New Template&lt;/strong&gt;. For the free version, you can only create 2 Email Templates.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqlkte646tgwvhlf9fakb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqlkte646tgwvhlf9fakb.png" alt="New Template"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can configure your Email Template as you want.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fc9hmrruajc7hk4d60qha.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fc9hmrruajc7hk4d60qha.png" alt="Configure Template"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the image above, template body corresponds with the payload I’m sending from my app. You should also specify the receiver email and you can add extra Bcc and CC.&lt;/p&gt;

&lt;p&gt;Next, we need to get 3 important credentials. The service ID, the template ID and your public key.&lt;/p&gt;

&lt;p&gt;To get the template ID, navigate to the settings tab of the template and you will find the template key. You can as well modify the template name here.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyf1q0adla8gcw1hy50fo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyf1q0adla8gcw1hy50fo.png" alt="Template ID"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Next, go to the &lt;strong&gt;Email Services&lt;/strong&gt; screen and you will find the service ID for the service we connected earlier.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3tnw0e0fvgrk0rytlpqz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3tnw0e0fvgrk0rytlpqz.png" alt="Service ID"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Next, go to &lt;strong&gt;Account&lt;/strong&gt; and under the General tab, you will find your public and private keys. We only need the public key.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fn5z0yh08h28r3v4uupwa.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fn5z0yh08h28r3v4uupwa.png" alt="Public and Private key"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Lastly, we go back to our code and update our onSubmit function and integrate emailjs into our code.&lt;br&gt;
To do this, in your App.tsx, &lt;code&gt;import emailjs from "@emailjs/browser";&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Then modify your onSubmit function as follow:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkhfc2oqhmfdpyt4ceq06.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkhfc2oqhmfdpyt4ceq06.png" alt="onSubmit Function"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Finally, your App.tsx should look like this:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

import styles from "./app.module.css";
import { useForm } from "react-hook-form";
import { useState } from "react";
import emailjs from "@emailjs/browser";

interface IFormInput {
  firstName: string;
  lastName: string;
  email: string;
  phone: string;
  message: string;
}
function App() {
  const [loading, setLoading] = useState(false);

  const {
    register,
    handleSubmit,
    reset,
    formState: { errors },
  } = useForm&amp;lt;IFormInput&amp;gt;();
  const onSubmit = (data: IFormInput) =&amp;gt; {
    setLoading(true);

    const templateParams = {
      ...data,
    };

    emailjs
      .send(
        "your_service_id",
        "your_template_id",
        templateParams,
        "your_public_key"
      )
      .then(() =&amp;gt; {
        reset();
        setLoading(false);
        alert("One of our agents will contact you soon!");
      });
  };

  return (
    &amp;lt;div className={styles.contact_form}&amp;gt;
      &amp;lt;h1&amp;gt;Contact Form&amp;lt;/h1&amp;gt;
      &amp;lt;p&amp;gt;Our friendly team would love to hear from you.&amp;lt;/p&amp;gt;

      &amp;lt;form className={styles.form} onSubmit={handleSubmit(onSubmit)}&amp;gt;
        &amp;lt;div className={styles.group}&amp;gt;
          &amp;lt;div className={styles.input_group}&amp;gt;
            &amp;lt;label htmlFor="firstName"&amp;gt;First Name&amp;lt;/label&amp;gt;
            &amp;lt;input
              type="text"
              {...register("firstName", { required: true })}
              placeholder="Enter your first name"
            /&amp;gt;
            {errors.firstName &amp;amp;&amp;amp; (
              &amp;lt;span className={styles.error}&amp;gt;This field is required&amp;lt;/span&amp;gt;
            )}
          &amp;lt;/div&amp;gt;
          &amp;lt;div className={styles.input_group}&amp;gt;
            &amp;lt;label htmlFor="lastName"&amp;gt;Last Name&amp;lt;/label&amp;gt;
            &amp;lt;input
              type="text"
              {...register("lastName", { required: true })}
              placeholder="Enter your last name"
            /&amp;gt;
            {errors.lastName &amp;amp;&amp;amp; (
              &amp;lt;span className={styles.error}&amp;gt;This field is required&amp;lt;/span&amp;gt;
            )}
          &amp;lt;/div&amp;gt;
        &amp;lt;/div&amp;gt;
        &amp;lt;div className={styles.input_group}&amp;gt;
          &amp;lt;label htmlFor="email"&amp;gt;Email Address&amp;lt;/label&amp;gt;
          &amp;lt;input
            type="email"
            {...register("email", { required: true })}
            placeholder="Enter your email address"
          /&amp;gt;
          {errors.email &amp;amp;&amp;amp; (
            &amp;lt;span className={styles.error}&amp;gt;This field is required&amp;lt;/span&amp;gt;
          )}
        &amp;lt;/div&amp;gt;
        &amp;lt;div className={styles.input_group}&amp;gt;
          &amp;lt;label htmlFor="phone"&amp;gt;Phone Number&amp;lt;/label&amp;gt;
          &amp;lt;input
            type="tel"
            {...register("phone", { required: true })}
            placeholder="+2348123456789"
          /&amp;gt;
          {errors.phone &amp;amp;&amp;amp; (
            &amp;lt;span className={styles.error}&amp;gt;This field is required&amp;lt;/span&amp;gt;
          )}
        &amp;lt;/div&amp;gt;
        &amp;lt;div className={styles.input_group}&amp;gt;
          &amp;lt;label htmlFor="message"&amp;gt;Message&amp;lt;/label&amp;gt;
          &amp;lt;textarea
            {...register("message", { required: true })}
            placeholder="Leave us a message..."
          /&amp;gt;
          {errors.message &amp;amp;&amp;amp; (
            &amp;lt;span className={styles.error}&amp;gt;This field is required&amp;lt;/span&amp;gt;
          )}
        &amp;lt;/div&amp;gt;

        &amp;lt;button disabled={loading} className={styles.button} type="submit"&amp;gt;
          {loading ? "Sending..." : "Send Message"}
        &amp;lt;/button&amp;gt;
      &amp;lt;/form&amp;gt;
    &amp;lt;/div&amp;gt;
  );
}

export default App;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;And that is it. You can now get new contact us messages directly in the receiver email you specified.&lt;/p&gt;

&lt;p&gt;Thank you for reading. If this was helpful, leave a like.&lt;/p&gt;

</description>
      <category>emailjs</category>
      <category>reacthookform</category>
      <category>typescript</category>
      <category>react</category>
    </item>
  </channel>
</rss>
