Making a Chat App with ReactJS and Firebase

Making a Chat App with ReactJS and Firebase

We'll be making a simple chat app which works like a youtube superchat, with ReactJS and Firebase in just 5 steps. So, let's jump right into it!

Note: I know the article seems really long but thats because I tried to explain each and every thing we will be doing throughout this tutorial

TOC:


Step 1: Creating the project.

Use the following command to get started: $ npx create-react-app chitchat Now, I used the name chitchat for my project but, you can use any name you like.

Step 2: Installing necessary modules

We'll be using the following modules in our project:

  • firebase -> allows us to use firebase in our project
  • react-firebase-hooks -> allows us to get data from our database in real time
  • tailwindcss [Optional] -> for styling our chat app

To install them, we need to first cd into our newly created project and then use the following command: $ npm install firebase react-firebase-hooks Optionally, install tailwindcss: $ npm install tailwindcss

Step 3: Setting up firebase

Our project now is all set up! But, we need to have somewhere to store the chats. That's where firebase comes in! We can store the chats and host our chat app with firebase.

Let's start by creating a new project at Firebase Console

Add a name for your project and you can enable analytics if you wish. Your should now see a screen like this after your project is created:

Firebase Console

Now, let's enable authentication to let users sign in with their google account. Click on the authentication tab on the sidebar and enable Google authentication.

Once the auth is set up, we need to set up a database for our project. Click on the Cloud Firestore tab in the sidebar and create a database.

Firebase Firestore Menu

Next, we need to edit the database rules so that only logged in users can chat. In the rules tab paste the following code:

rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    match /{document=**} {
      allow read, write: if false;
    }

    match /messages/{docID} {
        allow read: if request.auth.uid != null;
      allow create: if canCreateMessage();
    }

    // custom func to check if the user can send messages
    function canCreateMessage() {
        let isSignedIn = request.auth.uid != null;
      let isOwner = request.auth.uid == request.resource.data.uid;

      return isSignedIn && isOwner;
    }
  }
}

Database Rules

Now that our database is completely set up, we can work on the app interface now!

Step 4: Creating functions for the chat

We'll be working only with the App.js file, present in src directory of our project, so load that up in your favorite text editor.

We'll begin by importing all the necessary functions.

import React, { useEffect, useRef, useState } from 'react';

import firebase from 'firebase/app';
import 'firebase/firestore';
import 'firebase/auth';

import { useAuthState } from 'react-firebase-hooks/auth';
import { useCollectionData } from 'react-firebase-hooks/firestore';

Next, we need to add our firebase config which you can get from project settings by adding a web app as shown below:

Creating web app in firebase and getting config values

Add the config values to App.js:

firebase.initializeApp({
  apiKey: "AIzaSyAmQZaUtO_0LZ3b6-7r5VIQhPHW2z1iqxk",
  authDomain: "chitchat-aa31b.firebaseapp.com",
  databaseURL: "https://chitchat-aa31b.firebaseio.com",
  projectId: "chitchat-aa31b",
  storageBucket: "chitchat-aa31b.appspot.com",
  messagingSenderId: "544785049624",
  appId: "1:544785049624:web:5900f7045eea337b573b6d"
})

Lastly, we'll define auth and firestore variable to use in our project:

const auth = firebase.auth();
const firestore = firebase.firestore();

Step 5: Functions & Components

We'll start with App():

function App() {
  // checks if user is authenticated i.e. logged in
  const [user] = useAuthState(auth);

  return (
    <div>
      <SignOut />
      <section>
        {/* Shows chatroom if user is logged in
        else show signin page */}
        {user ? <ChatRoom /> : <SignIn />}
      </section>
    </div>
  );
}

Then, we'll define a sign-in function for our chat app with google auth.

function SignIn() {
  const signInWithGoogle = () => {
    const provider = new firebase.auth.GoogleAuthProvider();
    auth.signInWithPopup(provider);
  }
  return (
    <div>
      <button onClick={signInWithGoogle}>Sign In With Google</button>
    </div>
  )
}

While we are at it, let's also include a sign-out function.

function SignOut() {
  return auth.currentUser && (
    <div>
      <button onClick={() => auth.signOut()}>Sign Out</button>
    </div>
  )
}

Now comes the main part of our chat app, the chat room where users can post messages.

function ChatRoom() {
  // we will use this to scroll to bottom of chat on page-reload and after sending a message
  const dummy = useRef();
  const scrollToBottom = () => {
    dummy.current.scrollIntoView({ behavior: 'smooth' });
  }

  // getting the message and sorting them by time of creation
  const messagesRef = firestore.collection('messages');
  const query = messagesRef.orderBy('createdAt', 'asc').limitToLast(25);

  const [messages] = useCollectionData(query, {idField: 'id'});

  return (
    <div>
      <div>
        {/* we will loop over the message and return a
        ChatMessage component for each message */}
        {messages && messages.map(msg => <ChatMessage key={msg.id} message={msg} />)}
        <span ref={dummy}></span>
      </div>

      {/* Form to type and submit messages */}
      <form onSubmit={sendMessage}>
        <input value={formValue} onChange={(e) => setFormValue(e.target.value)} placeholder="Say something" />
        <button type="submit" disabled={!formValue}>send</button>
      </form>
    </div>
  )
}

Let's define our sendMessage function now!

const sendMessage = async (e) => {
    e.preventDefault();
    // gets name, userID and pfp of logged in user
    const { displayName, uid, photoURL } = auth.currentUser;

    await messagesRef.add({
      user: displayName,
      body: formValue,
      createdAt: firebase.firestore.FieldValue.serverTimestamp(),
      uid: uid,
      photoURL: photoURL
    })

    // resetting form value and scrolling to bottom
    setFormValue('');
    dummy.current.scrollIntoView({ behavior: 'smooth' });
  }

Now, the last thing for us is to define the ChatMessage component that will display the Chat messages.

function ChatMessage(props) {
  const { user, body, uid, photoURL, createdAt } = props.message;

    return (
        <div>
            <img src={photoURL || 'https://i.imgur.com/rFbS5ms.png'} alt="{user}'s pfp" />
        </div>
        <div>
            <p>{user}</p>
            <p>{body}</p>
      </div>
  )
}

Aaaaaand with that, our chat app is officially done! All that's left is to add css-styling to your content. You can then deploy the app to firebase using the following command: $ firebase init hosting && firebase deploy --only hosting

Checkout the chat app in action and here's the repo for the project: {% github volt9801/chitchat no-readme %}


If you made it this far, kudos to you! If you have any tips or suggestions, please leave them down in the comments.