<?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: Mark Woodson</title>
    <description>The latest articles on DEV Community by Mark Woodson (@mwoodson11).</description>
    <link>https://dev.to/mwoodson11</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%2F709357%2F51eb94a8-b6ad-4252-b5cb-ecdd2cdd50e2.jpg</url>
      <title>DEV Community: Mark Woodson</title>
      <link>https://dev.to/mwoodson11</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/mwoodson11"/>
    <language>en</language>
    <item>
      <title>React Scheduler using Google Calendar API</title>
      <dc:creator>Mark Woodson</dc:creator>
      <pubDate>Wed, 29 Apr 2026 02:43:36 +0000</pubDate>
      <link>https://dev.to/mwoodson11/react-scheduler-using-google-calendar-api-335a</link>
      <guid>https://dev.to/mwoodson11/react-scheduler-using-google-calendar-api-335a</guid>
      <description>&lt;p&gt;Hey team! I was recently working on a project that involved getting someone's personal calendar integrated into a React project. After looking into it a bit, I couldn't find anything that had a full documentation on how to do it, which inspired me to make this. So today, let's make a fully functioning calendar in React, and populate it with events from your own Google Calendar! &lt;/p&gt;

&lt;p&gt;Now this tutorial is broken up into a few parts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Firstly, we'll focus entirely on the front end, using sample data from a local json file.&lt;/li&gt;
&lt;li&gt;Next, we'll create the Firebase functions that use the Google Calendar API to retrieve events&lt;/li&gt;
&lt;li&gt;Lastly, we'll connect the two and talk about some future ideas you can add.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;This tutorial will assume you have some experience or familiarity with React.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Project Setup
&lt;/h2&gt;

&lt;p&gt;To begin, we'll start with a basic React project by running &lt;code&gt;npx create-react-app reservation-demo&lt;/code&gt; in the terminal and then opening the directory in your editor of choice.&lt;/p&gt;

&lt;p&gt;Next, we will take advantage of a few npm packages, so run the following script to install all the packages we'll need for this tutorial:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm install devextreme@24.1 devextreme-react@24.1 --save --save-exact
npm install dayjs@1.11.11 --save
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;While we can just use Javascript's native Date object, I found using dayjs for the Firebase functions to be a lot easier to use.&lt;/p&gt;

&lt;h2&gt;
  
  
  Create Calendar Component
&lt;/h2&gt;

&lt;p&gt;For the frontend, we will make use of the DevExtreme React Scheduler. This package gives us everything we need to get a working frontend to display our calendar events, and handle any additions, edits, and deletions for the events. Inside our &lt;strong&gt;App.js&lt;/strong&gt; file, replace the contents with the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import 'devextreme/dist/css/dx.light.css';
import { Scheduler, View, Editing } from 'devextreme-react/scheduler';

const App = () =&amp;gt; {
  return (
    &amp;lt;Scheduler defaultCurrentView='week'&amp;gt;
      &amp;lt;Editing /&amp;gt;
      &amp;lt;View 
        type="week"
        startDayHour={9}
        endDayHour={19}
      /&amp;gt;
      &amp;lt;View type="month" /&amp;gt;
    &amp;lt;/Scheduler&amp;gt;
  );
};

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

&lt;/div&gt;



&lt;p&gt;This code uses the DevExtreme package to get each component that we need. If you run this, you should see a scheduler with a view of the week from 9am-7pm, and the option to see it by month. If you're curious on how each component works and any additional props or components that you can use with this, the documentation for each component can be found in the &lt;a href="https://devexpress.github.io/devextreme-reactive/react/scheduler/docs/guides/getting-started/" rel="noopener noreferrer"&gt;DevExtreme Docs&lt;/a&gt; within the API Reference section.&lt;/p&gt;

&lt;h2&gt;
  
  
  Create Calendar Data
&lt;/h2&gt;

&lt;p&gt;For now, we will use some dummy data to populate our calendar, but by the end, we will use the data that we get from our API call. We'll add this data to a json file and then incorporate that into our Calendar logic. &lt;br&gt;
Now create a new file called &lt;strong&gt;appointments.js&lt;/strong&gt; and paste the following into the file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import dayjs from 'dayjs';

export const appointments = [
    {
      title: 'Website Re-Design Plan',
      startDate: dayjs('2023-07-16 9:35').toDate(),
      endDate: dayjs('2023-07-16 11:30').toDate(),
      id: 0,
      location: 'Room 1',
    }, {
      title: 'Book Flights to San Fran for Sales Trip',
      startDate: dayjs('2023-07-18 12:35').toDate(),
      endDate: dayjs('2023-07-18 13:30').toDate(),
      id: 1,
      location: 'Room 1',
    }
];
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This array contains each event that will display on the calendar.&lt;br&gt;
When we import this object to our &lt;strong&gt;App.jsx&lt;/strong&gt; file, we can populate our calendar by adding the object to the scheduler in our App.jsx 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;// previous imports above
import { appointments } from './appointments';
const App = () =&amp;gt; {
    const currentDate = dayjs('2023-07-16').toDate()
    return (
        &amp;lt;Scheduler
          dataSource={appointments}
          textExpr='title'
          defaultCurrentView='week'
          currentDate={currentDate}
          adaptivityEnabled={true}
        &amp;gt;
        // other components below
    );
}; 
export default App;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A couple notes on this block of code:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;For the props, we set textExpr to use 'title' as the key from the appointments to represent the 'subject' for each appointment (This is useful for part two).&lt;/li&gt;
&lt;li&gt;We add the 'currentDate' value to set the schedule to a date where we can see the dummy events in the calendar.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;By adding our &lt;code&gt;appointments&lt;/code&gt; to our scheduler and creating a default date near those event dates, we should see them in the calendar with the code running with &lt;code&gt;npm start&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Fwhuwvctlpyfucqnl9xen.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Fwhuwvctlpyfucqnl9xen.png" alt="Running sample of calendar" width="800" height="664"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Great! Everything should be up and running. You should be able to see the events that you created with the dummy data, and also add, edit, and remove events. One thing to note is that the data does not persist if you refresh the page, so be mindful of that.&lt;/p&gt;

&lt;p&gt;This concludes the bulk of the frontend portion (it wasn't much but it's honest work). But now we can dive into creating those backend functions  to populate the calendar.&lt;/p&gt;

&lt;h2&gt;
  
  
  Create Google Project
&lt;/h2&gt;

&lt;p&gt;We will now go create a Google Cloud Project so we can set up our Calendar API calls and Firebase Functions. Go to the&lt;br&gt;
&lt;a href="https://console.firebase.google.com" rel="noopener noreferrer"&gt;Firebase Console&lt;/a&gt; to create an account or sign in, and create a new project. You can give the project whatever name, but keep note of the PROJECT_ID that gets generated, since this will be our unique identifier.&lt;br&gt;
&lt;a href="https://media2.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%2Fvkxicrp98v8hf6xjur8p.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Fvkxicrp98v8hf6xjur8p.png" alt="Firebase Project Creation Screen" width="800" height="828"&gt;&lt;/a&gt;&lt;br&gt;
You can leave the Enable Google Analytics option on or turn it off, but I enabled mine during this demo. Select 'Create Project' and then you're project should be ready within a few minutes. This should also initialize the project in the &lt;a href="https://console.cloud.google.com/" rel="noopener noreferrer"&gt;Google Cloud Console&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Once that's ready, we will set up Firebase Function, which will be what our component calls to read our Google calendar's events and add new ones. Inside the root of our project, we will set up our Firebase structure with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm install -g firebase-tools
firebase login
firebase init
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the options presented, select Functions and hit Enter to continue. Afterwards, make sure to select your PROJECT_ID and hit Enter. Now, select Javascript for the language we are using, and hit Enter to finish the set up for our Functions. Once it's all finished, our Functions should be set-up! There should be a hello-world example in our &lt;code&gt;index.js&lt;/code&gt; file that you can test out if you want.&lt;/p&gt;

&lt;h2&gt;
  
  
  Create Google API Key
&lt;/h2&gt;

&lt;p&gt;This section will focus on setting up the credentials that are needed to get the backend working.&lt;br&gt;
Let's go to &lt;a href="https://console.cloud.google.com/" rel="noopener noreferrer"&gt;Google Cloud Console&lt;/a&gt; and select &lt;strong&gt;Go to APIs overview&lt;/strong&gt; and click Credentials. Create a new project with whatever name (my-calendar-demo for consistency). In the top bar, go to Create Credentials -&amp;gt; OAuth Client ID. If you get a warning to create an OAuth Client ID, click the &lt;em&gt;Configure consent screen button&lt;/em&gt; and provide a name and support email, and hit save. You should now be on the Credentials screen. Go to Application Type -&amp;gt; Web Application and give your credentials a name. In the Restrictions section, add the URI &lt;a href="https://developers.google.com/oauthplayground" rel="noopener noreferrer"&gt;https://developers.google.com/oauthplayground&lt;/a&gt; to &lt;strong&gt;Authorized redirect URIs&lt;/strong&gt;. Now we can hit Create. If successful, you should now see your clientId and client secret. Copy these since we'll need them in a second. Select OK and then  in the table that appears, download the json file so that we can add it to our project. If you lose the client ID and secret, you can retrieve them in this json file.&lt;br&gt;
Now we must configure our OAuth2 by going to the redirect added: &lt;a href="https://developers.google.com/oauthplayground" rel="noopener noreferrer"&gt;OAuth Playground&lt;/a&gt;.&lt;br&gt;
Click the Settings icon in the top right and select &lt;strong&gt;Use your own OAuth credentials&lt;/strong&gt;, so we can add our client ID and secret. Now in the left side column, we are going to search for and select &lt;strong&gt;Calendar API v3&lt;/strong&gt; and give it the scope of &lt;a href="https://www.googleapis.com/auth/calendar" rel="noopener noreferrer"&gt;https://www.googleapis.com/auth/calendar&lt;/a&gt; so that we can read and write to our calendar. Now click &lt;strong&gt;Authorize APIs&lt;/strong&gt; to move to Step 2. You should be taken to the Google sign-in page, so log in (thankfully just this once) and now it should return back to a screen like the following.&lt;br&gt;
&lt;a href="https://media2.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%2F10a23ytp8gmwtl5ix2j4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2F10a23ytp8gmwtl5ix2j4.png" alt="OAuth Playground Step Two" width="616" height="692"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Click the &lt;strong&gt;Exchange authorization code for tokens&lt;/strong&gt; button to get a refresh token, which will allow us access to the API calls we'll make. We'll add the refresh_token to the json file that we downloaded, so now it should look like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "web": {
    "client_id": "706881267217-852pu9orud6araf6k.apps.googleusercontent.com",
    "project_id": "my-calendar-demo-444701",
    "auth_uri": "https://accounts.google.com/o/oauth2/auth",
    "token_uri": "https://oauth2.googleapis.com/token",
    "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
    "client_secret": "&amp;lt;REDACTED&amp;gt;",
    "redirect_uris": [
      "https://developers.google.com/oauthplayground"
    ]
  },
  "refresh_token": "&amp;lt;REDACTED&amp;gt;"
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Get Calendar ID
&lt;/h2&gt;

&lt;p&gt;Lastly let's retrieve the ID for the calendar that we'll get and add events. Go to your &lt;a href="https://calendar.google.com/calendar/u/1/r?pli=1" rel="noopener noreferrer"&gt;Google Calendar&lt;/a&gt; and go to the settings of the calendar you want to use. In the settings, click the Integrate Calendar button and you should see the Calendar ID.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Fa6nhkrr9t8hcbmlu6plj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Fa6nhkrr9t8hcbmlu6plj.png" alt="Google Calendar settings with calendar ID" width="800" height="518"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Copy this and add it to the credentials.json file.&lt;/p&gt;

&lt;p&gt;Once we've done that, we are done with the tedious setup! Now let's do the finishing touches.&lt;/p&gt;

&lt;h2&gt;
  
  
  Set up the functions
&lt;/h2&gt;

&lt;p&gt;Now for our calendar to work how we want, we need to implement a read and write function to get the events on the calendar and add new ones. So back in our functions/index.js file, we are going to add the following code.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const { google } = require('googleapis');
const OAuth2 = google.auth.OAuth2; 
const calendar = google.calendar('v3');
const { onRequest } = require("firebase-functions/v2/https");
const { defineSecret } = require("firebase-functions/params");
const cors = require('cors')({ origin: true });
const credentials = require('./credentials.json');

const clientSecret = defineSecret("CLIENT_SECRET");
const refreshToken = defineSecret("REFRESH_TOKEN");

const READ_ERROR_RESPONSE = {
  status: "500",
  message: "There was an error reading your events on your Google calendar"
};

const ADD_ERROR_RESPONSE = {
  status: "500",
  message: "There was an error adding an event to your Google calendar"
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here we are instantiating the variables and constants that will be used for each of our functions.&lt;br&gt;
Next, we'll add the code for reading the calendar events:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const readEvents = (params, auth) =&amp;gt; {
  return calendar.events.list({
    auth: auth,
    timeMin: params.timeMin,
    timeMax: params.timeMax,
    calendarId: credentials.web.calendar_id,
    singleEvents: true,
    orderBy: 'startTime',
  }).then(res =&amp;gt; {
    console.log('Request successful');
    return res.data;
  }).catch(err =&amp;gt; {
    console.log('Rejecting because of error');
    throw new Error(err);
  })
};

exports.readEventsFromCalendar = onRequest(
  { secrets: [clientSecret, refreshToken] },
  async (request, response) =&amp;gt; {
    cors(request, response, () =&amp;gt; {
      const oAuth2Client = new OAuth2(
        credentials.web.client_id,
        process.env.CLIENT_SECRET,
        credentials.web.redirect_uris[0]
      );
      oAuth2Client.setCredentials({
        refresh_token: process.env.REFRESH_TOKEN,
      });
      const eventData = {
        timeMin: request.body.timeMin,
        timeMax: request.body.timeMax,
      };
      readEvents(eventData, oAuth2Client).then(data =&amp;gt; {
        response.status(200).send(data);
        return;
      }).catch(err =&amp;gt; {
        console.error('Error reading events: ' + err.message); 
        response.status(500).send(READ_ERROR_RESPONSE); 
        return;
      });
    });
  }
);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Our function uses the &lt;a href="https://developers.google.com/calendar/api/v3/reference/events/list" rel="noopener noreferrer"&gt;Events List&lt;/a&gt; Google Calendar call to get the details of the events in our calendar, with an optional timeMin and timeMax parameter to narrow the scope of our returned events.&lt;/p&gt;

&lt;p&gt;And finally, we'll add the code to publish new events in our calendar:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const addEvent = (event, auth) =&amp;gt; {
  return calendar.events.insert({
    auth: auth,
    calendarId: credentials.web.calendar_id,
    resource: {
      'summary': event.eventName,
      'description': event.description,
      'start': {
        'dateTime': event.startTime,
        'timeZone': event.timeZone,
      },
      'end': {
        'dateTime': event.endTime,
        'timeZone': event.timeZone,
      },
    },
  }).then(res =&amp;gt; {
    console.log('Request successful');
    return res.data;
  }).catch(err =&amp;gt; {
    console.log('Rejecting because of error');
    throw new Error(err);
  })
};

exports.addEventToCalendar = onRequest(
  { secrets: [clientSecret, refreshToken] },
  async (request, response) =&amp;gt; {
    cors(request, response, () =&amp;gt; {
      const oAuth2Client = new OAuth2(
        credentials.web.client_id,
        process.env.CLIENT_SECRET,
        credentials.web.redirect_uris[0]
      );

      oAuth2Client.setCredentials({
        refresh_token: process.env.REFRESH_TOKEN,
      });
      const eventData = {
        eventName: request.body.eventName,
        description: request.body.description,
        startTime: request.body.startTime,
        endTime: request.body.endTime,
        timeZone: request.body.timeZone,
      };
      addEvent(eventData, oAuth2Client).then(data =&amp;gt; {
        response.status(200).send(data);
        return;
      }).catch(err =&amp;gt; {
        console.error('Error adding event: ' + err.message); 
        response.status(500).send(ADD_ERROR_RESPONSE); 
        return;
      });
    });
  }
);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we are &lt;em&gt;almost&lt;/em&gt; ready to publish our functions, but first we want to make sure we publish this as safely as possible. Since our client secret and refresh key are things we don't really want to expose, we are going to take those out of the credentials file, before publishing. So copy those and put them somewhere for a second, because we'll come back to them in the next step. Now that your credentials should look like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "web": {
    "calendar_id": "a4f940ab772d782b18824b5cb114ce4b91412fb8b7631d0@group.calendar.google.com",
    "client_id": "706881267217-852cmqf4vrud6araf6k.apps.googleusercontent.com",
    "project_id": "my-calendar-demo-444701",
    "auth_uri": "https://accounts.google.com/o/oauth2/auth",
    "token_uri": "https://oauth2.googleapis.com/token",
    "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
    "redirect_uris": [
      "https://developers.google.com/oauthplayground"
    ]
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can run &lt;code&gt;firebase deploy --only functions&lt;/code&gt; to get our functions deployed. Once that's complete, we are almost ready to start testing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Create function secrets
&lt;/h2&gt;

&lt;p&gt;Let's go to [Google Cloud Console (&lt;a href="https://console.cloud.google.com/" rel="noopener noreferrer"&gt;https://console.cloud.google.com/&lt;/a&gt;) and select the Firebase project. In the navigation menu, go to Cloud Functions in the Serverless section and from here you should be able to see our two functions. Let's start with &lt;code&gt;addEventToCalendar&lt;/code&gt; by clicking on it, and in this view you should be able to see the details about our function. Click the edit button and go to &lt;strong&gt;Runtime, build, connections and security settings&lt;/strong&gt;. Now we'll go to the&lt;br&gt;
&lt;code&gt;SECURITY AND IMAGE REPO&lt;/code&gt; tab and click &lt;strong&gt;Add a secret reference&lt;/strong&gt;. In the first input, click the dropdown and Create New Secret, and add CLIENT_SECRET as the name and paste the value in the &lt;strong&gt;Secret value&lt;/strong&gt; section, and then click Create Secret. Now select this secret, change the Reference method to Exposed as environment variable, and for the path, add CLIENT_SECRET and then select Done. Now we will repeat these steps for REFRESH_TOKEN. After that, we have to do the same for our second function &lt;code&gt;readEventsFromCalendar&lt;/code&gt;. And once we complete all that, we can finally try and test it out.&lt;/p&gt;
&lt;h2&gt;
  
  
  Testing the functions
&lt;/h2&gt;

&lt;p&gt;So within our Function details for addEventToCalendar, there is a &lt;strong&gt;Testing&lt;/strong&gt; tab that we'll select and add the following payload:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;Once we add this and hit Test the Function, we should see a successful response, and if we check out calendar, we should see this new event!&lt;br&gt;
And just to check our other function, we'll go to the testing tab there and add this payload:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;And this success should show the information on the event we just added! If these are successful, now all that's left to do is just tie it all together!&lt;/p&gt;

&lt;h2&gt;
  
  
  Use with our app
&lt;/h2&gt;

&lt;p&gt;Now that we’ve published and tested our functions, we are going to make those function calls to populate the calendar in our app. We’re going to create a new file called &lt;code&gt;util.js&lt;/code&gt; and will add these lines of code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import dayjs from 'dayjs';

const parseCalendarEvents = (data) =&amp;gt; {
  const eventsList = data.items?.map(event =&amp;gt; ({
    startDate: dayjs(event.start.dateTime).toDate(),
    endDate: dayjs(event.end.dateTime).toDate(),
    title: event.summary,
    description: event.description,
    id: event.id,
  }))
  return {calendarTimeZone: data.timeZone, eventsList};
};

export const readCalendarEvents = async (timeMin, timeMax) =&amp;gt; {
  const requestBody = {
    timeMin,
    timeMax
  }
  return fetch('https://us-central1-my-calendar-demo-fa016.cloudfunctions.net/readEventsFromCalendar', {
    method: 'POST',
    body: JSON.stringify(requestBody),
    headers: { 'Content-Type': 'application/json' }
  })
  .then(res =&amp;gt; res.json())
  .then(json =&amp;gt; parseCalendarEvents(json))
  .catch(error =&amp;gt; console.log(error));
};

export const addToCalendar = async ({title, startDate, endDate, description, allDay}, timeZone) =&amp;gt; {
  const startTime = dayjs(startDate);
  const endTime = dayjs(endDate);
  const requestBody = {
    eventName: title,
    description,
    startTime: startTime.format('YYYY-MM-DDTHH:mm:ssZ'),
    endTime: endTime.format('YYYY-MM-DDTHH:mm:ssZ'),
    timeZone
  };
  return fetch('https://us-central1-my-calendar-demo-fa016.cloudfunctions.net/addEventToCalendar', {
    method: 'POST',
    body: JSON.stringify(requestBody),
    headers: { 'Content-Type': 'application/json' }
  }).catch(error =&amp;gt; console.log(error));
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;addToCalendar&lt;/code&gt; function calls our Firebase function &lt;code&gt;addEventToCalendar&lt;/code&gt; and readCalendarEvents calls &lt;code&gt;readEventsFromCalendar&lt;/code&gt; while using a helper function called &lt;code&gt;parseCalendarEvents&lt;/code&gt; to clean up the response and format the data to how we need it for our scheduler. &lt;/p&gt;

&lt;p&gt;Now we will call these functions in App.jsx. We will add useEffect to fetch the events once the module loads by adding:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;useEffect(() =&amp;gt; {
    const getAppointments = async () =&amp;gt; {
      const startTime = currentDate.subtract(2, 'month').format('YYYY-MM-DDTHH:mm:ss') + '.000Z';
      const endTime = currentDate.add(2, 'month').format('YYYY-MM-DDTHH:mm:ss') + '.000Z';
      const {calendarTimeZone, eventsList} = await readCalendarEvents(startTime, endTime);
      setApps(eventsList);
      setTimeZone(calendarTimeZone);
    };
    getAppointments();
 }, []);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So now your App.jsx file should look like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import 'devextreme/dist/css/dx.light.css';
import { useCallback, useState, useEffect } from 'react';
import { Scheduler, View, Editing } from 'devextreme-react/scheduler';
import { addToCalendar, readCalendarEvents } from './util';
import dayjs from 'dayjs';

const App = () =&amp;gt; {
  const currentDate = dayjs();
  const [apps, setApps] = useState([]);
  const [timeZone, setTimeZone] = useState('');

  useEffect(() =&amp;gt; {
    const getAppointments = async () =&amp;gt; {
      const startTime = currentDate.subtract(2, 'month').format('YYYY-MM-DDTHH:mm:ss') + '.000Z';
      const endTime = currentDate.add(2, 'month').format('YYYY-MM-DDTHH:mm:ss') + '.000Z';
      const {calendarTimeZone, eventsList} = await readCalendarEvents(startTime, endTime);
      setApps(eventsList);
      setTimeZone(calendarTimeZone);
    };
    getAppointments();
 }, []);

  const handleAppointment = useCallback(async (e) =&amp;gt; {
    await addToCalendar(e.appointmentData, timeZone);
  }, []);

  return (
    &amp;lt;Scheduler
      dataSource={apps}
      timeZone={timeZone}
      textExpr='title'
      defaultCurrentView='week'
      currentDate={currentDate.toDate()}
      adaptivityEnabled={true}
      onAppointmentAdding={handleAppointment}
    &amp;gt;
      &amp;lt;Editing /&amp;gt;
      &amp;lt;View 
        type="week"
        startDayHour={9}
        endDayHour={19}
      /&amp;gt;
      &amp;lt;View type="month" /&amp;gt;
    &amp;lt;/Scheduler&amp;gt;
  );
};

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

&lt;/div&gt;



&lt;p&gt;And with this we should be able to run the app and see how your calendar looks like (with or without events). But to fully test this out, lets double click a spot on the scheduler, add some event details, and click create. If no console log shows up for an error, we should have a successful API call, and we should see the newly created event in the calendar!&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;And with that, you should have a Calendar up and running, populated with your Google Calendar events! Now there's still a lot you can do to add onto this, like playing around with some more of the props that you can use with the scheduler, creating functions for updating and deleting events, adding a loading state for the read call, etc. Please let me know in replies if you have any questions but I hope this was helpful!&lt;/p&gt;

</description>
      <category>tutorial</category>
      <category>firebase</category>
      <category>react</category>
    </item>
    <item>
      <title>Grow your mailing list: Mailchimp x React x Firebase</title>
      <dc:creator>Mark Woodson</dc:creator>
      <pubDate>Fri, 13 Oct 2023 02:06:00 +0000</pubDate>
      <link>https://dev.to/mwoodson11/react-x-mailchimp-1fne</link>
      <guid>https://dev.to/mwoodson11/react-x-mailchimp-1fne</guid>
      <description>&lt;p&gt;For this tutorial, we're gonna make a React Subscriber form, which adds someone to an audience list in Mailchimp. We'll break it down to these steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create React form&lt;/li&gt;
&lt;li&gt;Create Mailchimp account&lt;/li&gt;
&lt;li&gt;Create serverless Mailchimp API function&lt;/li&gt;
&lt;li&gt;Combine the two&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;As always, I like to add a bit of context to the tutorial before starting, but if you want to skip ahead, feel free. &lt;/p&gt;

&lt;p&gt;A popular addition that I normally add to websites I create are Subscriber forms, and typically I use Mailchimp, since it's easy to work with and useful overall. Since it's a great tool to utilize, I felt like making a quick tutorial on how to set it up. I also wanted to make sure that you could either come away with:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;a reusable form that you can add to other projects&lt;/li&gt;
&lt;li&gt;a serverless function that you can call for other use cases&lt;/li&gt;
&lt;li&gt;why not both?&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Fiimv0ljcn4qea64l313p.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Fiimv0ljcn4qea64l313p.gif" alt="Cheering gif" width="200" height="152"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For the frontend, we will utilize Material UI to create the form and Firebase Functions to create the serverless function. &lt;a href="https://mui.com/material-ui/" rel="noopener noreferrer"&gt;Material UI&lt;/a&gt; is a component library that makes it much easier to create a form that you can continue to build upon afterwards. And &lt;a href="https://firebase.google.com/docs/functions" rel="noopener noreferrer"&gt;Firebase Functions&lt;/a&gt; is a serverless framework where we can run store our backend code and trigger our functions upon HTTPS requests. To answer any potential questions.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Why Firebase? Because I'm familiar with it and the project I was working on already had a Firebase project set up. You can use another Serverless architecture, or even run it locally with an Express server. But this tutorial will use Firebase.&lt;/li&gt;
&lt;li&gt;Why even make a serverless function? Since Firebase will be using an API Key, keeping it stored in a serverless architecture can be much safer than having it in the client, especially if there's plans for this function in the future. So we're going with best practices.
Any other questions, please ask in the comments and I'll answer! But anyways, let's jump into the tutorial.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Create Simple Input Form &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;Firstly, let's create our standard React project by running&lt;br&gt;
&lt;code&gt;npx create-react-app mailchimp-form&lt;/code&gt;. Once it finishes, run &lt;code&gt;npm install @mui/material @emotion/react @emotion/styled&lt;/code&gt; to install the dependencies for what we need for the frontend.&lt;/p&gt;

&lt;p&gt;Once it's created, let's remove the code inside the App.jsx file so that it's pretty barebones. It should look like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import React from 'react';

function App() {

return (
    &amp;lt;div&amp;gt;Hello!&amp;lt;/&amp;gt;
)

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

&lt;/div&gt;



&lt;p&gt;So now let's create the form to get all the user info we want. Let's add the following to the App.jsx file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import React, { useState } from 'react';
import { TextField, Button, Container, Stack } from '@mui/material';

function App() {
  return (
    &amp;lt;Container&amp;gt;
      &amp;lt;h2&amp;gt;Mailchimp Form&amp;lt;/h2&amp;gt;
      &amp;lt;form onSubmit={handleSubmit} &amp;gt;
        &amp;lt;Stack spacing={{ xs: 3, sm: 2 }} direction={{ xs: 'column', sm: 'row' }} sx={{mb: 3}}&amp;gt;
          &amp;lt;TextField
            name='firstName'
            type="text"
            variant='outlined'
            label="First Name"
            onChange={handleUserChange}
            value={firstName}
            fullWidth
            required
          /&amp;gt;
          &amp;lt;TextField
            name='lastName'
            type="text"
            variant='outlined'
            label="Last Name"
            onChange={handleUserChange}
            value={lastName}
            fullWidth
            required
          /&amp;gt;
        &amp;lt;/Stack&amp;gt;
        &amp;lt;TextField
          name='email'
          type="email"
          variant='outlined'
          label="Email"
          onChange={handleUserChange}
          value={email}
          fullWidth
          required
          sx={{mb: 3}}
        /&amp;gt;
        &amp;lt;Button variant="outlined" type="submit"&amp;gt;Subscribe&amp;lt;/Button&amp;gt;
      &amp;lt;/form&amp;gt;
    &amp;lt;/Container&amp;gt;
  )
}

export default App;

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

&lt;/div&gt;



&lt;p&gt;And if we see the app, it should look like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Fjhfpjhy93sc4o0osknrb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Fjhfpjhy93sc4o0osknrb.png" alt="Mailchimp Form" width="638" height="456"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Great, so now we have our form showing up, but at the moment, it doesn't keep track of the user input. So let's add that piece, let's add &lt;code&gt;useState&lt;/code&gt; to fully keep track of what a user inputs.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const [user, setUser] = useState({
    firstName: '',
    lastName: '',
    email: '',
});

const { firstName, lastName, email } = user;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now for the form to update the state of our userInfo, we'll add this function.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  const handleUserChange = (e) =&amp;gt; {
    setUser({
      ...user,
      [e.target.name]: e.target.value
    })
  };
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Lastly, let's add a function to handle the submission when the user clicks the subscribe button:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const handleSubmit = async (e) =&amp;gt; {
    e.preventDefault();
    console.log(user);
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, our code should look like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import React, { useState } from 'react';
import { TextField, Button, Container, Stack, Snackbar, Alert, Skeleton } from '@mui/material';

function App() {

  const [user, setUser] = useState({
    firstName: '',
    lastName: '',
    email: '',
  });

  const { firstName, lastName, email } = user;

  const handleUserChange = (e) =&amp;gt; {
    setUser({
      ...user,
      [e.target.name]: e.target.value
    })
  };

  const handleSubmit = async (e) =&amp;gt; {
    e.preventDefault();
    console.log(user);
  };

  return (
    &amp;lt;Container&amp;gt;
      &amp;lt;h2&amp;gt;Mailchimp Form&amp;lt;/h2&amp;gt;
      &amp;lt;form onSubmit={handleSubmit} &amp;gt;
        &amp;lt;Stack spacing={{ xs: 3, sm: 2 }} direction={{ xs: 'column', sm: 'row' }} sx={{mb: 3}}&amp;gt;
          &amp;lt;TextField
            name='firstName'
            type="text"
            variant='outlined'
            label="First Name"
            onChange={handleUserChange}
            value={firstName}
            fullWidth
            required
          /&amp;gt;
          &amp;lt;TextField
            name='lastName'
            type="text"
            variant='outlined'
            label="Last Name"
            onChange={handleUserChange}
            value={lastName}
            fullWidth
            required
          /&amp;gt;
        &amp;lt;/Stack&amp;gt;
        &amp;lt;TextField
          name='email'
          type="email"
          variant='outlined'
          label="Email"
          onChange={handleUserChange}
          value={email}
          fullWidth
          required
          sx={{mb: 3}}
        /&amp;gt;
        &amp;lt;Button variant="outlined" type="submit"&amp;gt;Subscribe&amp;lt;/Button&amp;gt;
      &amp;lt;/form&amp;gt;
    &amp;lt;/Container&amp;gt;
  )
}

export default App;

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

&lt;/div&gt;



&lt;p&gt;With this, our form takes, stores, and submits the user input! But submits to what? Currently, nothing. But then enters Mailchimp.&lt;/p&gt;

&lt;h2&gt;
  
  
  Create Mailchimp Account
&lt;/h2&gt;

&lt;p&gt;Let's create a Mailchimp audience form. Now first we need to go to Mailchimp.com and sign up for an account. If you already have an account, awesome! If not, no worries. When you sign up, it does ask for a your business email and business address. It does need this info for anti-spam laws. If you don't have a business location, you can use one of the &lt;a href="https://mailchimp.com/help/alternative-physical-address-ideas/" rel="noopener noreferrer"&gt;alternate address suggestions&lt;/a&gt; that it recommends. Or you can just skip this part entirely, bookmark it, and use it in the future when you create one for yourself or a future client.&lt;br&gt;
Once your account is setup, go to the Audience Tab -&amp;gt; Signup Forms. On this page, go to Settings -&amp;gt; Audience Name and Defaults. On this page, you should see an Audience ID, which is what we'll need in the next step, so take note of it.&lt;br&gt;
Now one more thing we'll want to check out is in Settings -&amp;gt; Audience fields and &lt;em&gt;MERGE&lt;/em&gt; tags. &lt;br&gt;
&lt;a href="https://media2.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%2F6nkmfeybygrm2mpalpzi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2F6nkmfeybygrm2mpalpzi.png" alt="Merge tags screen" width="800" height="487"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here we can see the fields that we can collect from our subscribers, and add any additional ones. We'll stick with the ones we have here, which is First Name, Last Name, and Email Address.&lt;/p&gt;
&lt;h2&gt;
  
  
  Create Serverless function
&lt;/h2&gt;

&lt;p&gt;Now that we have our form and Mailchimp account set up, let's connect to it with a serverless function using Firebase Functions. &lt;/p&gt;

&lt;p&gt;Now we'll initialize a firebase project. I'll write the quick steps here but for a description of the steps and more about firebase I'll point you to &lt;a href="https://firebase.google.com/docs/functions/get-started?gen=2nd" rel="noopener noreferrer"&gt;the documentation&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create Project in the &lt;a href="https://console.firebase.google.com/" rel="noopener noreferrer"&gt;Firebase Console&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Install the Firebase CLI with &lt;code&gt;npm install -g firebase-tools&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Log into Firebase from your terminal with &lt;code&gt;firebase login&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;At the root of our project, run &lt;code&gt;firebase init functions&lt;/code&gt;, select Javascript for our language, and install the dependencies&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Now that should set us up. Now we'll add our function into the index.js file within the functions directory:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
exports.addSubscriber = functions
.runWith({ secrets: ["MAILCHIMP_SECRET_KEY"] })
.https.onCall(async (data, context) =&amp;gt; {
  const audienceId = ""; // add your audienceId here

  const {firstName, lastName, email} = data;
  const mailchimp = new Mailchimp(process.env.MAILCHIMP_SECRET_KEY);
  try {
    const resp = await mailchimp.post(`/lists/${audienceId}/members`, {
      merge_fields: {
        "FNAME": firstName,
        "LNAME": lastName
      },
      email_address: email,
      status: "subscribed"
    });
    return resp;
  } catch (error) {
    throw new functions.https.HttpsError("unknown", error);
  }
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So all that our function is doing is destructing our request body for the parameters that we'll use for the Mailchimp call, and then returning a response indicating if it's successful.&lt;br&gt;
Now one thing to note, we'll have to add that MAILCHIMP_SECRET_KEY to your secrets in Firebase so before you deploy, you'll just add the secret with &lt;code&gt;firebase functions:secrets:set MAILCHIMP_SECRET_KEY&lt;/code&gt; and then paste the key. Afterwards, you can deploy with &lt;code&gt;npm run deploy&lt;/code&gt; and once it succeeds, you should see the URL of the function that we'll use with our form. &lt;br&gt;
If you want to just run it locally, first you can remove the &lt;code&gt;.runWith({ secrets: ["MAILCHIMP_SECRET_KEY"] })&lt;/code&gt; line and paste your key in place of &lt;strong&gt;process.env.MAILCHIMP&lt;/strong&gt;. And then you can run &lt;code&gt;npm run serve&lt;/code&gt; to start the function emulator, which will give you a URL for the function that you can use locally.&lt;br&gt;
To test this, we can send a request in Postman with a request body of:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
    "data": {
        "firstName": "Mark",
        "lastName": "Woodson",
        "email": "mark@markwoodson.me"
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can verify the success of the request through the response or checking your Mailchimp Audience list to see if it grew by one!&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Side note: The reason the request is in a field called 'data' is because the way we created the Firebase function. By using the "onCall" method, the request needs to be sent in that format. Using "onRequest", you can make the request without that field at the top level.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Alright, now we got two pretty useful, but separate, components. But how about we combine them into one awesome piece. &lt;/p&gt;

&lt;h2&gt;
  
  
  Bring It All Together
&lt;/h2&gt;

&lt;p&gt;Let's make the call to our Firebase function. All we need to do is make a change to our submit function. Let's change the submit function to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const handleSubmit = async (e) =&amp;gt; {
    e.preventDefault();
    fetch('http://127.0.0.1:5001/mailchimp-form/us-central1/addSubscriber', {
      method: 'POST',
      headers: {
        "Content-Type": "application/json"
      },
      body: JSON.stringify({ firstName, lastName, email }),
    })
    .then((response) =&amp;gt; response.json())
    .then((data) =&amp;gt; {
      if (data.status === 'subscribed') {
        setUser({
          firstName: '',
          lastName: '',
          email: '',
        });
      } else {
        console.error('Error:', data);
      }
    })
    .catch((error) =&amp;gt; {
      console.error('Error:', error);
    });
  };
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After running the app, you should be good to go!&lt;/p&gt;

&lt;h2&gt;
  
  
  Next Steps
&lt;/h2&gt;

&lt;p&gt;Awesome work on this. I hope it worked smoothly and will serve you well in the future! Now this definitely can be improved with a couple of things, like error handling, styling, additional form inputs, etc. But if you had any comments, questions, or concerns, please let me know in the comments and check out my other work at &lt;a href="https://markwoodson.me/" rel="noopener noreferrer"&gt;markwoodson.me&lt;/a&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>react</category>
      <category>firebase</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Create Deployment Pipeline for React App on Hostinger</title>
      <dc:creator>Mark Woodson</dc:creator>
      <pubDate>Sat, 23 Apr 2022 17:37:49 +0000</pubDate>
      <link>https://dev.to/mwoodson11/create-deployment-pipeline-for-react-app-on-hostinger-5bc9</link>
      <guid>https://dev.to/mwoodson11/create-deployment-pipeline-for-react-app-on-hostinger-5bc9</guid>
      <description>&lt;p&gt;Today, I'll go over the steps needed to create a pipeline to deploy a React app to Hostinger so that whenever you push to your Github repository, your changes will automatically be deployed to your website. This tutorial will dive deeper into deploying React apps onto Hostinger, so if you do not know how to do so, please check out my previous tutorial: &lt;a href="https://dev.to/mwoodson11/deploy-react-app-on-hostinger-3id5"&gt;Deploy React App on Hostinger&lt;/a&gt;.&lt;br&gt;
The steps in this tutorial will be broken down into:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create Git repository for code&lt;/li&gt;
&lt;li&gt;Set up Git configuration on Hostinger&lt;/li&gt;
&lt;li&gt;Configure Github to push to Hostinger&lt;/li&gt;
&lt;li&gt;Add Github actions to your repository&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;
  
  
  1. Create Git repository for code
&lt;/h2&gt;

&lt;p&gt;First thing you'll need to do is create a repo for your code on Github. Whether you choose to create a public or private repo, you'll have to do some different steps in the future, so be mindful.&lt;br&gt;
&lt;a href="https://media2.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%2F4lagztolremw3vfsztmq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2F4lagztolremw3vfsztmq.png" alt="Github repo" width="800" height="450"&gt;&lt;/a&gt;&lt;br&gt;
Once you have the repo created, add, commit, and push your changes to the repo. There's many resources online to help you with this. But if this is your first time, the following commands should be all you need to do:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;echo "# test-repo" &amp;gt;&amp;gt; README.md
git init
git add README.md
git commit -m "first commit"
git branch -M main
git remote add origin git@github.com:sample/react.git #change to your Github repository
git push -u origin main
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once that's finished and you see your code in your repository, you're good to go to the next step!&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Set up Git configuration on Hostinger
&lt;/h2&gt;

&lt;p&gt;Next we'll set up the Git configurations for Hostinger. Back on Hostinger, you'll need to go to the GIT configuration settings for your website. So go to Hosting -&amp;gt; (your website) -&amp;gt; scroll to the Advanced section and select GIT.&lt;br&gt;
&lt;a href="https://media2.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%2Fq9lsstj178vz4koto9m0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Fq9lsstj178vz4koto9m0.png" alt="Git settings on Hostinger" width="800" height="398"&gt;&lt;/a&gt;&lt;br&gt;
First, we will go to the Create a Repository section and add your git repo (private repo's should look like &lt;a href="mailto:git@github.com"&gt;git@github.com&lt;/a&gt;:sample/react.git and public repo's should look like &lt;a href="https://github.com/sample/react.git" rel="noopener noreferrer"&gt;https://github.com/sample/react.git&lt;/a&gt;). For branch, put &lt;code&gt;build&lt;/code&gt;. The reason for this is when we push our code to Github, we want the build of the code to be sent to Hostinger, not the code itself (the last step will cover this).&lt;br&gt;
&lt;a href="https://media2.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%2F0e3vclsqx1xe7gkznttd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2F0e3vclsqx1xe7gkznttd.png" alt="Hostinger Git Configuration" width="800" height="376"&gt;&lt;/a&gt;&lt;br&gt;
If this branch does not exist, that's okay. If you currently have a branch for &lt;code&gt;build&lt;/code&gt;, make sure you are ok with it being used to have the builds pushed to this branch for the future. Note: if you already have contents in your public_html folder, you will have to delete the contents inside, but we will quickly get everything back in there soon enough!&lt;br&gt;
In the Manage Repositories section, click Auto Deployment and note the Webhook URL, since we will use it in the next step. &lt;br&gt;
&lt;a href="https://media2.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%2Fjfw86bzgekm3oukrzm75.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Fjfw86bzgekm3oukrzm75.png" alt="Webhook URL Hostinger" width="800" height="208"&gt;&lt;/a&gt;&lt;br&gt;
If your repo is private, continue with this next step, but if not you can skip to step 3. &lt;br&gt;
Scroll up to the Private Git Repository and click the generate SSH Key button. Keep note of key, since we will use it in the next step.&lt;/p&gt;
&lt;h2&gt;
  
  
  3. Configure Github to push to Hostinger
&lt;/h2&gt;

&lt;p&gt;Next, we'll set up your repository to send your builds to Hostinger whenever you push to your repo. First on Github, go to your settings on your repository and then select Webhooks. Paste the Webhook URL from the previous step into the payload URL input and leave the content type as application/x-www-form-urlencoded. You don't need to add a secret, and you can leave everything else as is (just make sure that the &lt;code&gt;Just the push event option&lt;/code&gt; is selected. Select &lt;code&gt;Add webhook&lt;/code&gt; and then you're almost done.&lt;br&gt;
&lt;a href="https://media2.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%2Fmk4pe1mn60gvvykvgkcy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Fmk4pe1mn60gvvykvgkcy.png" alt="Github Webhook" width="800" height="302"&gt;&lt;/a&gt;&lt;br&gt;
Now if your repo is private, you'll have to follow this next step, otherwise you can skip to the final part. &lt;br&gt;
Now to successfully allow Hostinger to pull your build, you need to go to &lt;code&gt;Deploy keys&lt;/code&gt; in the repo settings and paste the ssh key that was generated in the previous step. You can title it &lt;code&gt;Hostinger key&lt;/code&gt; and it does not need write access, so you can leave the &lt;code&gt;allow write access&lt;/code&gt; box unchecked.&lt;br&gt;
&lt;a href="https://media2.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%2Fspt3sh5rgli0gjrw8a25.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Fspt3sh5rgli0gjrw8a25.png" alt="Github Deploy keys" width="800" height="356"&gt;&lt;/a&gt;&lt;br&gt;
Once you click &lt;code&gt;Add key&lt;/code&gt; you're good to go to the final step.&lt;/p&gt;
&lt;h2&gt;
  
  
  4. Add Github action to repository
&lt;/h2&gt;

&lt;p&gt;Lastly, we will set up Github Actions to build your code whenever you push to your repo. Now all you need to do is create a folder called &lt;code&gt;.github/workflows&lt;/code&gt; from the root, and add a &lt;code&gt;publish.yml&lt;/code&gt; file in there. &lt;br&gt;
&lt;a href="https://media2.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%2F0qenhngeoyh4drngsui0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2F0qenhngeoyh4drngsui0.png" alt="Github Actions Workflow" width="800" height="397"&gt;&lt;/a&gt;&lt;br&gt;
Post the following code into this file, and if you are not using the &lt;code&gt;build&lt;/code&gt; branch, then make sure to change lines 26-27 to the name of the branch you plan to use:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;name: Generate a build and push to another branch

on:
  push:
    branches:
      - main

jobs:
  build:
    runs-on: ubuntu-latest
    name: Build and Push
    steps:
      - name: git-checkout
        uses: actions/checkout@v3

      - name: Install all dependencies
        run: npm install

      - name: Build
        run: npm run build # The build command of your project

      - name: Push
        uses: s0/git-publish-subdir-action@develop
        env:
          REPO: self
          BRANCH: build # The branch name where you want to push the assets
          FOLDER: build # The directory where your assets are generated
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # GitHub will automatically add this - you don't need to bother getting a token
          MESSAGE: "Build: ({sha}) {msg}" # The commit message
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once you add this, add, commit, and push the change. &lt;br&gt;
Your pipeline should be fully set up so whenever you push a change to your main branch, this should automatically update your website on Hostinger!&lt;/p&gt;

&lt;p&gt;If you have any comments or questions, please leave a reply or reach out to me on &lt;a href="https://markwoodson.me" rel="noopener noreferrer"&gt;my portfolio&lt;/a&gt;!&lt;/p&gt;

</description>
      <category>react</category>
      <category>devops</category>
      <category>tutorial</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Deploy React App on Hostinger</title>
      <dc:creator>Mark Woodson</dc:creator>
      <pubDate>Sun, 07 Nov 2021 23:05:57 +0000</pubDate>
      <link>https://dev.to/mwoodson11/deploy-react-app-on-hostinger-3id5</link>
      <guid>https://dev.to/mwoodson11/deploy-react-app-on-hostinger-3id5</guid>
      <description>&lt;p&gt;Today, I'll show you the steps needed to deploy a react app to &lt;a href="https://www.hostinger.com/web-hosting" rel="noopener noreferrer"&gt;Hostinger&lt;/a&gt;. The tutorial will be broken down into the following steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create and Build React app&lt;/li&gt;
&lt;li&gt;Configure Hostinger account for deployment&lt;/li&gt;
&lt;li&gt;Troubleshooting&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Once you complete this, I have a follow up on creating a Continuous Deployment pipeline so that you can automatically publish your changes once you push to Git. &lt;a href="https://dev.to/mwoodson11/create-deployment-pipeline-for-react-app-on-hostinger-5bc9"&gt;Check that out here!&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  1. Create and Build React app
&lt;/h1&gt;

&lt;p&gt;The point of this tutorial is to mainly focus on the deployment of a React app onto Hostinger, so the app we'll create will be the default app created for a new app.&lt;/p&gt;

&lt;h3&gt;
  
  
  Create React App
&lt;/h3&gt;

&lt;p&gt;Open your terminal and in the directory that you wish to create the app, type &lt;code&gt;npx create-react-app hostinger-react-app&lt;/code&gt; for a new app called "hostinger-react-app" (or whatever you choose to call your app). Once it has finished installing, you should be able to run &lt;code&gt;npm start&lt;/code&gt; in the terminal and see the following in your browser at &lt;code&gt;localhost:3000&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Fh9540sbv0x0xa8yyucmm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Fh9540sbv0x0xa8yyucmm.png" alt="Default React App" width="800" height="673"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you're able to see the above, then you are all set to build.&lt;/p&gt;

&lt;h3&gt;
  
  
  Build React App
&lt;/h3&gt;

&lt;p&gt;For your app to deploy correctly on Hostinger, you will need to have your app point to your domain. &lt;/p&gt;

&lt;p&gt;Add &lt;code&gt;"homepage": "https://hostinger-react-app.com"&lt;/code&gt; to the &lt;code&gt;package.json&lt;/code&gt; file, so it will look like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "name": "hostinger-react-app",
  "version": "0.1.0",
  "homepage": "https://hostinger-react-app.com",
  "private": true,
  "dependencies": { 
  ...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now run &lt;code&gt;npm run build&lt;/code&gt; in your terminal to create the production build of your app, that will be used to deploy to Hostinger. When the build completes, you should see a build folder and this in the terminal:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2F03dfzipdcdswc916x9ze.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2F03dfzipdcdswc916x9ze.png" alt="Build terminal" width="800" height="498"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you do not see &lt;code&gt;The project was built assuming it is hosted at https://hostinger-react-app.com/.&lt;/code&gt; (a problem that I had when first built my app), then you can configure the homepage another way. If you do see this line, you can skip to the Compress section.&lt;/p&gt;

&lt;p&gt;Add a new file called &lt;code&gt;.env&lt;/code&gt; at the root level of your project, so that it's at the same level of your &lt;code&gt;package.json&lt;/code&gt;. Inside the file, add &lt;code&gt;PUBLIC_URL=https://hostinger-react-app.com&lt;/code&gt;. Now, when you run &lt;code&gt;npm run build&lt;/code&gt;, you should see it points the domain.&lt;/p&gt;

&lt;h3&gt;
  
  
  Compress
&lt;/h3&gt;

&lt;p&gt;Lastly, go to you build folder and compress all the files into a zip folder. This will be needed when we add the files to Hostinger.&lt;/p&gt;

&lt;p&gt;At this point, your app is ready to be deployed to Hostinger.&lt;/p&gt;

&lt;h1&gt;
  
  
  2. Configure Hostinger account for deployment
&lt;/h1&gt;

&lt;p&gt;In your Hostinger panel, navigate to the Website tab and select Import Website. Select the zipped folder to upload your files into the &lt;code&gt;public_html&lt;/code&gt; folder. Once it's finished uploading, go to the file manager to check that your files are present. &lt;/p&gt;

&lt;p&gt;Lastly, you'll need to add a &lt;code&gt;.htaccess&lt;/code&gt; file inside your &lt;code&gt;public_html&lt;/code&gt; folder with the following code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;IfModule mod_rewrite.c&amp;gt;
  RewriteEngine On
  RewriteBase /
  RewriteRule ^index\.html$ - [L]
  RewriteCond %{REQUEST_FILENAME} !-f
  RewriteCond %{REQUEST_FILENAME} !-d
  RewriteCond %{REQUEST_FILENAME} !-l
  RewriteRule . /index.html [L]
&amp;lt;/IfModule&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There are other ways to write this &lt;code&gt;.htaccess&lt;/code&gt; file, but in my experience, this configuration makes sure that apps that use routing will work.&lt;/p&gt;

&lt;p&gt;After that, you should be able to navigate to the domain and see your app running!&lt;/p&gt;

&lt;h1&gt;
  
  
  Troubleshooting
&lt;/h1&gt;

&lt;p&gt;I added some troubleshooting steps in each section to make sure your on track to having your app work, but in case the app doesn't show once you're done, here's a couple more tips.&lt;/p&gt;

&lt;p&gt;In case when you go to your domain and a page like this shows:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Ftbpm7d6kvnggxm4y12k6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Ftbpm7d6kvnggxm4y12k6.png" alt="Default page" width="590" height="334"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Navigate to the DNS Zone Editor and make sure the content of you A record is pointing to your account's IP Address (shown in the left panel on your home page).&lt;/p&gt;

&lt;p&gt;If you're still having problems, I suggest reaching out to the Hostinger chat support, to make sure your account is set up and good to go!&lt;/p&gt;

&lt;p&gt;If you have any questions or issues, please leave a comment!&lt;/p&gt;

</description>
      <category>react</category>
      <category>webdev</category>
      <category>beginners</category>
      <category>tutorial</category>
    </item>
  </channel>
</rss>
