How To Build A Real-Time Chat App With ReactJS and Firebase
How To Build A Real-Time Chat App With ReactJS and Firebase
In the app, we will allow the user to log in with their Google account
using Firebase's Google sign-in Authentication. We will also store and
retrieve all the chatroom messages using Firebase's Cloud Firestore.
Prerequisites
You should have Node.js installed on your system. You should also
have intermediate knowledge of CSS, JavaScript, and Reactjs. Finally,
you should know how to use the command terminal. You do not need
to know how to use Firebase.
What is Firebase?
Firebase is a Backend-as-a-Service (Baas). It is a Google-backed
Forum app Donate
development platform that allows developers to build iOS, Android,
Learn to code — free 3,000-hour curriculum
and web apps.
Firebase Authentication
Firebase Authentication (SDK) is a Firebase tool that supports
different authentication methods like passwords, phone numbers,
Google, Facebook, Twitter, Github, and more. In this app, we will be
using the Google sign-in Authentication.
Cloud Firestore
Cloud Firestore is a cloud-based NoSQL database server for storing
and syncing data. It stores data in documents as key-value pairs, and
the documents are organized into collections.
Now that you have an idea of how Firebase and Cloud Firestore work,
let's build our app.
Note: For this article's scope, I have already prewritten the CSS and
prebuilt the components for the chat app. You can find the final
Forum Donate
project code on GitHub and the CSS and components in the setup
Learn to code — free 3,000-hour curriculum
folder. You can also view the final project with this live link.
Alternatively, you can create your React app by running the code in
your terminal, npx create-react-app react-chat , to create it. Here,
react-chat is the name of the app. Then you can run npm install
firebase react-firebase-hooks to install firebase and react-
firebase-hooks.
Delete the current src folder and replace it with the one from the
setup folder to use the prewritten CSS and prebuilt components.
(Optionally, you can write yours yourself.)
an img folder where the Google Sign-in image for the Sign-in
button is stored,
the new App.js file with all our components imported into it,
Run npm start to view the app in the browser. Our app should look
like this:
Now let's create a Firebase account and set up our Firebase project.
Create a project
Choose the type of app where you want to add Firebase. For this
article, we chose the code icon because we are building a web app.
Forum Donate
Then select npm , copy the code snippet below it (we will be using it
later), and click Continue to console.
Code snippet
Click on Get started and select Google in the Sign-in method's tab.
Enable Google, choose your Project support email, and click Save.
Test mode means that any client will have read/write access to your
database for 30 days. Production mode means that no one will have
read/write access to your database. You will have to edit your rules to
grant access to specific clients.
Select a location where you want your Firestore to be stored and click
Enable. It will default to the location closest to you.
Rule to be replaced
Replace the current rule with the code below and click on Publish.
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /{document=**} {
allow read: if true;
allow create, update, delete, write: if request.auth != null;
}
}
}
The allow read: if true; means that anyone can read your
database. The allow create, update, delete, write: if
request.auth != null; means only authenticated clients can create,
update, delete, and write to your database.
Enter the collection’s name, for example “messages”, and click Next.
You can either click Auto_ID to generate an id for the doc or enter one
yourself.
After that, create the key-value pairs of the Document. The Field
input represents the key name, the Type defines what type of data it is
(string, number, timesStamp, and so on), and the Value is the value of
the key.
You can also add more documents by clicking the plus sign, otherwise,
click Save to save your collection. Forum Donate
Our Firebase project is set. Let's go back to our React app.
Learn to code — free 3,000-hour curriculum
// Import the functions you need from the SDKs you need
import { initializeApp } from "firebase/app";
// TODO: Add SDKs for Firebase products that you want to use
// https://firebase.google.com/docs/web/setup#available-libraries
import { getAuth } from "firebase/auth";
import { getFirestore } from "firebase/firestore";
// Initialize Firebase
const app = initializeApp(firebaseConfig);
export const auth = getAuth(app);
export const db = getFirestore(app);
Forum Donate
After the user successfully signs in, their data is saved in auth , and
the user is redirected to our app. The signOut function clears the
auth data, returning it to null . The new user state also determines
which authentication buttons are rendered to the user.
Let's also add authentication to our App.js file. Import the following:
Add the new user state so we can use it to render the Welcome
component if the user is not logged in or the Chatbox component if
the user is logged in.
const [user] = useAuthState(auth);
Forum Donate
function App() {
const [user] = useAuthState(auth);
return (
<div className="App">
<NavBar />
{!user ? <Welcome /> : <ChatBox />}
</div>
);
}
export default App;
Testing our new sign-in and sign-out functions, we see the following:
App demo
Now let's do the same for our Welcome Component, which currently
has the following code:
import React from "react";
Forum Donate
import GoogleSignin from "../img/btn_google_signin_dark_pressed_web
return (
<main className="welcome">
<h2>Welcome to React Chat.</h2>
<img src="/logo512.png" alt="ReactJs logo" width={50} height=
<p>Sign in with Google to chat with with your fellow React De
<button className="sign-in">
<img
onClick={googleSignIn}
src={GoogleSignin}
alt="sign in with google"
type="button"
/>
</button>
</main>
);
};
export default Welcome;
return (
<form className="send-message">
<input
...
value={message}
onChange={(e) => setMessage(e.target.value)}
/>
<button type="submit">Send</button>
</form>
);
};
return (
<form onSubmit={(event) => sendMessage(event)} className="send-
...
These key-value pairs are what make up the data for our document.
After this is done, it then resets the message state to an empty string.
We create a useEffect hook that will run anytime changes are made
in the chatroom, like sending or deleting a message.
useEffect(() => {
const q = query(
collection(db, "messages"),
orderBy("createdAt"),
limit(50)
);
Forum Donate
const unsubscribe = onSnapshot(q, (QuerySnapshot) => {
Learn to code — free 3,000-hour curriculum
let messages = [];
QuerySnapshot.forEach((doc) => {
messages.push({ ...doc.data(), id: doc.id });
});
setMessages(messages);
});
return () => unsubscribe;
}, []);
The forEach loop loops through all the documents from the
collection and saves the data in the new array. It then sets the initial
messages array to the new messages array.
{messages?.map((message) => (
<Message key={message.id} message={message} />
))}
The final code looks like this: Forum Donate
useEffect(() => {
const q = query(
collection(db, "messages"),
orderBy("createdAt"),
limit(50)
);
const unsubscribe = onSnapshot(q, (QuerySnapshot) => {
let messages = [];
QuerySnapshot.forEach((doc) => {
messages.push({ ...doc.data(), id: doc.id });
});
setMessages(messages);
});
return () => unsubscribe;
}, []);
return (
<main className="chat-box">
<div className="messages-wrapper">
{messages?.map((message) => (
<Message key={message.id} message={message} />
))}
</div>
<SendMessage />
</main>
);
};
export default ChatBox;
Forum Donate
return (
<div
className={`chat-bubble ${message.uid === user.uid ? "right"
<img
className="chat-bubble__left"
src={message.avatar}
alt="user avatar"
/>
<div className="chat-bubble__right">
<p className="user-name">{message.name}</p>
<p className="user-message">{message.text}</p>
</div>
</div>
);
};
export default Message;
We also conditioned a CSS style to take effect based on the uid of the
message's author. So if the message’s author uid is the same as the uid
of the person logged in, then the CSS styles stored in theForum
selector right
Donate
should be added
Currently, to the div.
all messages are Otherwise, nothe
positioned to newleft,
style
so should be added.
if the logged user
Learn to code — free 3,000-hour curriculum
is the message’s author, their message should be positioned to the
right. Let's view these changes in our browser:
The message is sent and stored in our database. Then all the messages
are retrieved, and the chatroom is updated in real-time with the new
messages.
The name and avatar of the user are also present on the message card.
But we can also see that the chat doesn't scroll to the bottom when a
new message enters. Let's fix that.
We then go into the Messages component, access the scroll const, and
add scroll.current.scrollIntoView({ behavior: "smooth" }) to
the bottom of our sendMessage function.
This code tells the browser to let the scroll span be in view in the
browser after sending a message. That is why the span tag was placed
at the bottom of all the messages.
Going back to the browser, we should see the chat scroll to the
bottom when the user sends in a new message.
Forum Donate
Wrapping Up
Demo showing chat scrolling to the bottom with a new message
You can find the code for this project on GitHub, and you can explore
the chat room using this live link.
Timonwa Akintokun
Timonwa is a Frontend Developer and Technical Writer passionate about
Forum
making the web accessible to everyone. She writes articles on web
Donate
development that help beginners have an easier learning journey.
Learn to code — free 3,000-hour curriculum
If you read this far, tweet to the author to show them you care.
Tweet a thanks
ADVERTISEMENT
Our mission: to help people learn to code for free. We accomplish this by creating thousands of
videos, articles, and interactive coding lessons - all freely available to the public. We also have
thousands of freeCodeCamp study groups around the world.
Donations to freeCodeCamp go toward our education initiatives, and help pay for servers,
Forum Donate
services, and staff.
Learn to code
You can make a tax-deductible — freehere.
donation 3,000-hour curriculum
Trending Guides
Our Charity
About Alumni Network Open Source Shop Support Sponsors Academic Honesty