Here's how I made a real-time chatbox in ReactJS and Firebase
Tags: react, firebase, javascript
Published: 2021-01-30 12:08:40

![Cover Image](https://dev-to-uploads.s3.amazonaws.com/i/59uxt3nuilkgv570acco.gif)

Hello!
I recently learned using Firebase with ReactJS and thought of implementing my knowledge by creating some project. Since, I wanted to implement my firebase knowledge fast, I decided to make a very simple chat box instead of making very highly complex App which would take ages to complete.

In this post, I will share exactly how I made the live/real-time chatbox with Google OAuth. For this tutorial, we will not be using Context API or redux for holding state. We will be holding the state in the component's state. 

## Creating a new react App.
```bash
npx create-react-app live-chatbox
```
## Installing necessary dependencies
```
npm install firebase-tools -g
npm install firebase
```

## Setting up a Firebase project
1. Follow [this](https://www.youtube.com/watch?v=vUe91uOx7R0) Youtube tutorial to learn how to create a new firebase project.
2. Copy the config from the firebase console and create a new file **"firebase/config.js"** and paste in it. It would look somthing like this. (You can also copy your config data into .env.local file and use it here.)
```javascript
// For Firebase JS SDK v7.20.0 and later, measurementId is optional
const firebaseConfig = {
  apiKey: "your apiKey",
  authDomain: "your authDomain",
  projectId: "your projectId",
  storageBucket: "your storageBucket",
  messagingSenderId: "your messagingSenderId",
  appId: "your appId",
  measurementId: "your measurementId",
};
``` 
3.Now we just have to initialize and export some variables in this file. First of all, import firebase from **firebase** package and then initialise firebase app using `firebase.intiliazeApp(config)`. Also create and export two variables to initialize authentication and firestore. The final code would look like this.
```javascript
import firebase from "firebase";

// For Firebase JS SDK v7.20.0 and later, measurementId is optional
const firebaseConfig = {
  apiKey: "your apiKey",
  authDomain: "your authDomain",
  projectId: "your projectId",
  storageBucket: "your storageBucket",
  messagingSenderId: "your messagingSenderId",
  appId: "your appId",
  measurementId: "your measurementId",
};

// initialize firebase
const firebaseApp = firebase.initializeApp(firebaseConfig);

// initialize authentication
export const auth = firebase.auth();
//initialize firestore
export const db = firebase.firestore();
```

## Creating the App UI
For this project, we will be having a very similar app layout which you can see below.
![App Structure](https://dev-to-uploads.s3.amazonaws.com/i/9nn7nr7pohjdf4qufuqy.png)

1. Create a components folder (inside src folder) and create two components, __Chat.js__ and __Message.js__. Also create corresponding .css files. The folder structure should now look something like this.
![Folder structure](https://dev-to-uploads.s3.amazonaws.com/i/vfzdc0el2i4201zq1u4k.png)
2. Let's edit App.js file and write some actual code. In this file, we will be rendering components on the basis of whether user is logged in or not. If the user if logged in, then render the Chat component, else render the Login button in the view. Also, as mentioned earlier we will be storing user information into the component state. 

```javascript
import { useState } from "react";
import Chat from "./components/Chat";
import "./App.css";

const App = () => {
  const [user, setUser] = useState(null);

  return user !== null ? (
    <Chat user={user} />
  ) : (
    <div className="login">
      <h1>Login</h1>
      <button>Login with Google</button>
    </div>
  );
};

export default App;

```
3.Now let's edit the **Chat.js** and **Message.js** file to make the UI look complete.
```javascript
import { useState, useRef } from "react";
import Message from "./Message";
import "./Chat.css";



const Chat = ({ user }) => {
  const [input, setInput] = useState("");
  const [messages, setMessages] = useState([]);
  const scrollRef = useRef();

  return (
    <div className="chat__box">
      <div className="chat__header">
        <img src={user.photoURL} alt="User avatar" />
        <p>{user.displayName}</p>
      </div>
      <div className="chat__messages">
        {messages.map((message) => (
          <Message key={message.id} message={message} />
        ))}
        <div
          ref={scrollRef}
          style={{ float: "left", clear: "both", paddingTop: "4rem" }}
        ></div>
      </div>
      <div className="chat__input">
        <form>
          <input
            type="text"
            placeholder="Type a message here"
            value={input}
            onChange={(e) => setInput(e.target.value)}
          />
          <button>&rarr;</button>
        </form>
      </div>
    </div>
  );
};

export default Chat;
```
4.Now let's edit the Message.js to finally complete our App UI.
```javascript

import "./Message.css";

const Message = ({ message }) => {
  return (
    <div className="chat__message">
      <img src={message.user.photoURL} alt="User avatar" />
      <p>{message.message}</p>
    </div>
  );
};

export default Message;

```
## Firebase (The fun part 🔥)
1. Now we will be coding the authentication and message sending part. In our App.js file,import **firebase** from firebase package and **db** variable from the config file create a **loginWithGoogle()** function and attach it as an onClick listener to the button. Also we will use **useEffect** hook to run a function everytime when the page renders. This function will log us back into the google account on page refresh. The code for final App.js is something like this.

```javascript
import { useState, useEffect } from "react";
import Chat from "./components/Chat";
import { auth } from "./firebase/config";
import firebase from "firebase";
import "./App.css";

const App = () => {
  const [user, setUser] = useState(null);

  useEffect(() => {
    auth.onAuthStateChanged(user => {
      if (user) {
        setUser(user);
      }
    })
  }, [])

  const loginWithGoogle = () => {
    const provider = new firebase.auth.GoogleAuthProvider();
    auth.signInWithPopup(provider).then((result) => {
      setUser(result.user);
    });
  };

  return user !== null ? (
    <Chat user={user} />
  ) : (
    <div className="login">
      <h1>Login</h1>
      <button onClick={loginWithGoogle}>Login with Google</button>
    </div>
  );
};

export default App;
```
2.In our Chat component, let's add a useEffect() hook to read the firestore database on every change and take a snapshot of it so that we can retreive the new data in real-time. 
```javascript
import { useState, useEffect, useRef } from "react";
import { db } from "../firebase/config";
import firebase from "firebase";
import "./Chat.css";
import Message from "./Message";

const Chat = ({ user }) => {
  const [input, setInput] = useState("");
  const [messages, setMessages] = useState([]);
  const scrollRef = useRef();

  useEffect(() => {
    db.collection("messages")
      .orderBy("timestamp", "asc")
      .onSnapshot((snapshot) => {
        setMessages(
          snapshot.docs.map((doc) => ({ id: doc.id, ...doc.data() }))
        );
      });
  }, []);

  return (
    <div className="chat__box">
      <div className="chat__header">
        <img src={user.photoURL} alt="User avatar" />
        <p>{user.displayName}</p>
      </div>
      <div className="chat__messages">
        {messages.map((message) => (
          <Message key={message.id} message={message} />
        ))}
        <div
          ref={scrollRef}
          style={{ float: "left", clear: "both", paddingTop: "4rem" }}
        ></div>
      </div>
      <div className="chat__input">
        <form onSubmit={sendMessages}>
          <input
            type="text"
            placeholder="Type a message here"
            value={input}
            onChange={(e) => setInput(e.target.value)}
          />
          <button>&rarr;</button>
        </form>
      </div>
    </div>
  );
};

export default Chat;
```
3.Now our app is ready to recieve messages, and now let's create the messaging functionality. For that, let's create a sendMessage() function that will create a message object (only if input is not empty) and then add it to the firebase firestore db. It will also scroll the view accordingly down to the bottom after every message. Our final code in Chat component will look like this.
```javascript
import { useState, useEffect, useRef } from "react";
import { db } from "../firebase/config";
import firebase from "firebase";
import "./Chat.css";
import Message from "./Message";

const Chat = ({ user }) => {
  const [input, setInput] = useState("");
  const [messages, setMessages] = useState([]);
  const scrollRef = useRef();

  useEffect(() => {
    db.collection("messages")
      .orderBy("timestamp", "asc")
      .onSnapshot((snapshot) => {
        setMessages(
          snapshot.docs.map((doc) => ({ id: doc.id, ...doc.data() }))
        );
      });
  }, []);

  const sendMessages = (e) => {
    e.preventDefault();

    if (input !== "") {
      const newMessage = {
        message: input,
        user: { displayName: user.displayName, photoURL: user.photoURL },
        timestamp: firebase.firestore.FieldValue.serverTimestamp(),
      };

      db.collection("messages").add(newMessage);

      setInput("");

      scrollRef.current.scrollIntoView({ behavior: "smooth" });
    }
  };

  return (
    <div className="chat__box">
      <div className="chat__header">
        <img src={user.photoURL} alt="User avatar" />
        <p>{user.displayName}</p>
      </div>
      <div className="chat__messages">
        {messages.map((message) => (
          <Message key={message.id} message={message} />
        ))}
        <div
          ref={scrollRef}
          style={{ float: "left", clear: "both", paddingTop: "4rem" }}
        ></div>
      </div>
      <div className="chat__input">
        <form onSubmit={sendMessages}>
          <input
            type="text"
            placeholder="Type a message here"
            value={input}
            onChange={(e) => setInput(e.target.value)}
          />
          <button>&rarr;</button>
        </form>
      </div>
    </div>
  );
};

export default Chat;
```
And now our app is finally ready to be published.

Note - All the **.css** files can be found in the GitHub repo mentioned below.

GitHub Repo 👇
[https://github.com/shaan71845/live-chatbox](https://github.com/shaan71845/live-chatbox)

Live Demo 👇
[https://live-chatbox-26e1b.web.app/](https://live-chatbox-26e1b.web.app/)