Create a real-time chat application using react JS and firebase

Google firebase service provides us to create awesome web applications without having a server. So, we are going to create our chat application using firebase where we will use authentication, firestore, hosting. So, without wasting any time, let’s get started.

Create a react project:

I am assuming that you have installed node js installed on your system. If not, then download and install it first. To create a react project, run this command-

npx create-react-app firebase-chat-app
cd firebase-chat-app
yarn start or npm run dev

Architecture of project

Our app will have two pages. One is for authentication, where users can log in or signup using email and password, And the other for a chat, where users can send messages.

//src\App.jsimport { useEffect, useState } from 'react'
import './App.css'
import Auth from './Pages/Auth'
import Chat from './Pages/Chat'
function App() {
const [isLoading, setIsLoading] = useState(true)
const [isAuthenticated, setIsAuthenticated] = useState(false)
useEffect(() => {
setIsLoading(false)
}, [])
return isLoading ? (
<div
className="d-flex text-white vh-100 vw-100
justify-content-center align-items-center"
>
Loading...
</div>
) : (
<div>{isAuthenticated ? <Chat /> : <Auth />}</div>
)
}
export default App

Create an authentication page:

For the authentication page, we have created auth page (src\Pages\Auth\index.js), where the user can choose to log in or sign up.

import React, { useState } from 'react'
import Login from './Login'
import Signup from './Signup'
const Auth = () => {
const [showLoginPage, setShowLoginPage] = useState(false)
return (
<div>
<div style={{ maxWidth: 400, width: '90%' }}>
<div className="text-center">
<h2>{showLoginPage ? 'Log in' : 'Signup'}</h2>
{showLoginPage ? (
<small>
Not registered?, click
<span onClick={() => setShowLoginPage(false)}>here</span>
to register.
</small>
) : (
<small>
Already registered?, click
<span onClick={() => setShowLoginPage(true)}>here</span>
to login.
</small>
)}
</div>
{showLoginPage ? <Login /> : <Signup />}
</div>
</div>
)
}
export default Auth

Sign up page:

The signup page will have a first name, last name, email, password. All fields will be mandatory. When a user submits the signup form, we can use

firebase.auth().createUserWithEmailAndPassword(email, password)
<form onSubmit={handleFormSubmit}>
<div className="row mx-0 my-3">
<div className="col-6">
<label className="form-label">First Name</label>
<input
name="firstName"
value={formDetails.firstName}
className="form-control"
onChange={handleFormChanges}
/>
</div>
<div className="col-6">
<label className="form-label">Last Name</label>
<input
name="lastName"
value={formDetails.lastName}
className="form-control"
onChange={handleFormChanges}
/>
</div>
<div className="col-12">
<label className="form-label">Email</label>
<input
name="email"
value={formDetails.email}
className="form-control"
onChange={handleFormChanges}
/>
</div>
<div className="col-12">
<label className="form-label">Password</label>
<input
name="password"
value={formDetails.password}
className="form-control"
type="password"
onChange={handleFormChanges}
/>
</div>
</div>
<div className=" d-flex p-2">
<button type="submit" className="ms-auto btn btn-primary ">
Register
</button>
</div>
</form>
const handleFormChanges = (e) => {
formDetails[e.target.name] = e.target.value
setFormDetails({ ...formDetails })
}
const handleFormSubmit = async (e) => {
e.preventDefault()
const { firstName, lastName, email, password } = formDetails
if (!firstName || !lastName || !email || !password) {
alert('Please fill all fields!')
return
}
try {
const { user } = await firebase.auth().createUserWithEmailAndPassword(email, password)
if (user) {
user.updateProfile({
displayName: firstName + ' ' + lastName,
})
await firebase.firestore().collection('users').doc(user.uid).set({
firstName,
lastName,
email,
createdAt: Date.now(),
})
}
} catch (e) {
console.log(e)
alert(e.message)
}
}

Login Page

Now we will create a login screen with email and password fields. We can use

firebase.auth().signInWithEmailAndPassword(email, password)
<form onSubmit={handleFormSubmit}>
<div className="row mx-0 my-3">
<div className="col-12">
<label className="form-label">Email</label>
<input
name="email"
value={formDetails.email}
className="form-control"
onChange={handleFormChanges}
/>
</div>
<div className="col-12">
<label className="form-label">Password</label>
<input
name="password"
value={formDetails.password}
className="form-control"
type="password"
onChange={handleFormChanges}
/>
</div>
</div>
<div className=" d-flex p-2">
<button type="submit" className="ms-auto btn btn-primary ">
Login
</button>
</div>
</form>
const handleFormSubmit = async (e) => {
e.preventDefault()
const { email, password } = formDetails
if (!email || !password) {
alert('Please fill all fields!')
return
}
try {
await firebase.auth().signInWithEmailAndPassword(email, password)
} catch (e) {
console.log(e)
alert(e.message)
}
}
useEffect(() => {
firebase.auth().onAuthStateChanged((user) => {
if (user) {
setIsAuthenticated(true)
} else {
setIsAuthenticated(false)
}
setIsLoading(false)
})
}, [])

Chat Page

On a chat screen, we will have a logout button, welcome message, message box, send message text area.

<div className="d-flex vh-100 vw-100 justify-content-center align-items-center">
<div style={{ maxWidth: 600, width: '90%' }} className="rounded shadow p-2 text-white bg-dark">
<div className="d-flex">
<span className="ms-auto btn btn-danger btn-sm">Logout</span>
</div>
<div className="text-center">
<h1>Chat</h1>
<p>
Welcome
<span className="text-warning">{user.displayName}</span> !
</p>
</div>
<div className="bg-white rounded" style={{ height: 350 }}></div>
<div>
<form className="d-flex ">
<textarea
className="form-control mt-1 bg-light"
placeholder="Enter a message"
rows={1}
></textarea>
<div>
<span className=" btn btn-primary mt-1 ms-1">Send</span>
</div>
</form>
</div>
</div>
</div>

Sending a message

First, we need to create a message collection to store all messages. Then schema for messages will we-

{
"sender": user.uid,
"senderName": user.displayName,
"message": string,
"createdAt": timestamp
}
const messageRef = firebase.firestore().collection('messages')const handleMessageInput = (e) => {
setUserMessage(e.target.value)
}
const handleSendMessage = (e) => {
e.preventDefault()
if (userMessage) {
messageRef
.set({
sender: user.uid,
senderName: user.displayName,
message: userMessage,
createAt: Date.now(),
})
.then(() => {
setUserMessage('')
})
.catch((e) => {
console.log(e)
alert(e)
})
}
}

Showing message and get the message in realtime

Firebase provides a onSnapshot method. By using this method, we can get messages in real-time.

const [messages, setMessages] = useState([])useEffect(() => {
const getAllMessages = messageRef.onSnapshot((doc) => {
let tempMessages = []
doc.docChanges().forEach((change) => {
if (change.type === 'added') {
tempMessages.push(change.doc.data())
}
})
setMessages((prevState) => [...prevState, ...tempMessages])
})
return () => getAllMessages()
}, [])

Display messages

<div className="bg-white rounded " style={{ height: 350, overflowY: 'auto' }}>
{messages.length ? (
messages.map((message, i) => (
<div className="d-flex" key={i}>
<div
className={`m-2 p-2 rounded w-75 ${
message.sender === user.uid ? 'ms-auto text-dark' : 'bg-dark text-white'
}`}
style={{ background: '#E8F6EF' }}
>
<div style={{ fontSize: 11 }}>
<span className="text-muted">{message.senderName}</span>
<span className="float-end text-muted">
{new Date(message.createdAt).toUTCString()}
</span>
</div>
{message.message}
</div>
</div>
))
) : (
<div className="text-center pt-5">No message found!</div>
)}
</div>

Output-

Logout

To logout the user, we can user firebase.auth().signOut(). After that, onAuthStateChanged function will be executed which is written in app.js and will set isAuthenticated to false.

const handleLogout = () => {
firebase.auth().signOut()
}

Conclusion

Now our app is ready to deploy. In addition, you can add a loader where needed.

--

--

Full Stack developer & Technical writer

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store