Welcome folks today in this blog post we will be building a public chat
messenger in react.js using firestore database with authentication in browser using javascript. All the full source code of the application is given below.
Get Started
In order to get started you need to a empty react.js project by executing the below command
npx create-react-app firestorechat
cd firestorechat
Installing Dependencies
Now we will install the required dependencies for this react.js chat project which are as follows
- Material UI Core
- Firebase
- React Firebase Hooks
npm i firebase
npm i @material/ui-core
npm i material-ui
npm i react-firebase-hooks
Folder structure for this project
First of all let’s see the folder structure and the required files required for this project
Getting Firebase Credentials
Now we will be getting the firebase credentials for this chat firestore project. First of all create a Firebase account and create a new project and go to the project settings and create a web app and get the required below code as shown below in the picture
Now create a firebase.js file inside the root directory of your react.js project and copy paste the below code
firebase.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
import firebase from 'firebase' const firebaseApp = firebase.initializeApp({ apiKey: "", authDomain: "", databaseURL: "", projectId: "", storageBucket: "", messagingSenderId: "", appId: "" }) const db = firebaseApp.firestore() const auth = firebase.auth() export { db, auth } |
In the above code you need to copy paste your config information of the firebase account. Here we are importing the firebase dependency and then we are calling the firestore method and also importing the auth method for authentication. Lastly we are exporting the auth and db variables from this config file.
Enable the Google SignIn Method
Now we need to go to the Authentication tab and inside the sign in methods go to google authentication and enable the sign in method of google as shown below
Enabling the Firestore Database
Now we need to enable the firestore database as well after that edit the rules and copy paste the below rules inside it section as shown below
1 2 3 4 5 6 7 8 |
rules_version = '2'; service cloud.firestore { match /databases/{database}/documents { match /{document=**} { allow read, write } } } |
Now you need to copy paste the below code inside the App.js file of your react.js project.
App.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
import './App.css'; import Chat from './components/Chat'; import SignIn from './components/SignIn'; import { auth } from './firebase.js' import { useAuthState } from 'react-firebase-hooks/auth' function App() { const [user] = useAuthState(auth) return ( <> {user ? <Chat /> : <SignIn />} </> ); } export default App; |
As you can see we are importing the required firebase react hooks to check the state of the authentication of user whether the user is logged in or not. If the user is logged in then we will show the chats component and if not we will show the login page. Now we need to make the required components in the next step.
Creating the Components
Now create a components folder inside the react.js project and inside it first of all make a SignIn.js
Component where we will have a login button to allow users to sign with their google account
Components/SignIn.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
import React from 'react' import firebase from 'firebase' import { auth } from '../firebase.js' import { Button } from '@material-ui/core' function SignIn() { function signInWithGoogle() { const provider = new firebase.auth.GoogleAuthProvider() auth.signInWithPopup(provider) } return ( <div style={{ display: 'flex', justifyContent: 'center', height: '100vh', alignItems: 'center' }}> <Button style={{ padding: '30px', fontSize: '20px', borderRadius: '0', fontWeight: '600' }} onClick={signInWithGoogle}>Sign In With Google</Button> </div> ) } export default SignIn |
As you can see we are using the google sign in authentication here. We have a simple login with google button rendering in the browser. When the user click on the button a popup
window will appear containing all the google accounts of the user. If the user selects any individual account then it will redirect the user to the chats component.
Now we will be building the Chat.js
component to render the input field where the user will enter the chat messages and also it will render all the messages which are entered by the user and it will extract it from firestore database.e
Components/Chat.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
import React, { useState, useEffect, useRef } from 'react' import { db, auth } from '../firebase' import SendMessage from './SendMessage' import SignOut from './SignOut' function Chat() { const scroll = useRef() const [messages, setMessages] = useState([]) useEffect(() => { db.collection('messages').orderBy('createdAt').limit(50).onSnapshot(snapshot => { setMessages(snapshot.docs.map(doc => doc.data())) }) }, []) return ( <div> <SignOut /> <div className="msgs"> {messages.map(({ id, text, photoURL, uid }) => ( <div> <div key={id} className={`msg ${uid === auth.currentUser.uid ? 'sent' : 'received'}`}> <img src={photoURL} alt="" /> <p>{text}</p> </div> </div> ))} </div> <SendMessage scroll={scroll} /> <div ref={scroll}></div> </div> ) } export default Chat |
As you can see we have the input field first of all to allow the user to enter the message and then we have a button on the right hand side to submit the form. When we submit the form and enter automatically message is passed to a separate component which we will make which is called as SendMessage.js here we are passing a scroll parameter to it. Scroll parameter is assigned a useRef() hook.Inside this sendMessage component we will insert the message inside the firestore database and automatically the height of the scrollbar or window will increase. It will automatically scroll down the window when you type a new message or receive a new message from the other User
Now we will write this New component which is called SendMessage.js inside the components folder
Components/SendMessage.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
import React, { useState } from 'react' import { db, auth } from '../firebase' import firebase from 'firebase' import { Input, Button } from '@material-ui/core' function SendMessage({ scroll }) { const [msg, setMsg] = useState('') async function sendMessage(e) { e.preventDefault() const { uid, photoURL } = auth.currentUser await db.collection('messages').add({ text: msg, photoURL, uid, createdAt: firebase.firestore.FieldValue.serverTimestamp() }) setMsg('') scroll.current.scrollIntoView({ behavior: 'smooth' }) } return ( <div> <form onSubmit={sendMessage}> <div className="sendMsg"> <Input style={{ width: '78%', fontSize: '15px', fontWeight: '550', marginLeft: '5px', marginBottom: '-3px' }} placeholder='Message...' type="text" value={msg} onChange={e => setMsg(e.target.value)} /> <Button style={{ width: '18%', fontSize: '15px', fontWeight: '550', margin: '4px 5% -13px 5%', maxWidth: '200px'}} type="submit">Send</Button> </div> </form> </div> ) } export default SendMessage |
Now you can see we have a simple input field and a button inside this component. We have a form element and we have attached a onSubmit event handler to the form. Whenever you click the send message button this event will trigger and after that inside that sendMessage() function we are getting the message value and inserting to firestore database. Here we are creating the collection or table inside firestore database which is called as messages and also we are also getting the user id and profile picture from google account object.
Now if you check your firebase dashboard area a new user will be entered like this as shown below
Styling the Chat App
Now guys for the styling just copy paste these css styles inside your App.css file of your react.js chat project
App.css
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 |
img { border-radius: 50%; height: 45px; margin-top: -20px; border: 2px solid black; } p { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif; font-weight: 500; font-size: 25px; margin-top: 10px; margin-left: 10px; margin-right: 10px; overflow-wrap: break-word; } .msg { display: flex; padding: 20px 10px 0 20px; margin: 20px; border-radius: 3000px; box-shadow: 0 0 10px rgb(164, 164, 164); align-items: center; } .sent { background-color: #395dff; color: white; border-top-right-radius: 1000px; flex-direction: row-reverse; padding: 20px 20px 0 10px; text-align: end; float: right; } .received { border: 1px solid lightgray; background-color: #FAFAFA; border-top-left-radius: 1000px; float: left; } .sendMsg { position: fixed; display: flex; width: 100%; bottom: 0; z-index: 1; border-top: 1px solid lightgray; margin-left: -5px; padding: 10px; padding-bottom: 30px; background-color: #fafafa; } .msgs { margin: 110px 0; display: flex; flex-direction: column; } @media (max-width: 775px) { p { font-size: 20px; } .sent { padding: 10px 10px 0 10px; } .received { padding: 10px 10px 0 10px; } img { height: 35px; margin-top: -10px; } } @media (max-width: 500px) { p { font-size: 15px; } .sent { padding: 7px 7px 0 7px; } .received { padding: 7px 7px 0 7px; } img { height: 30px; margin-top: -7px; } } |
And also if you try to send a message then this message will be shown inside the firestore database as shown below. A new collection will be created automatically
As you can see inside this figure we have also a signout button also appearing inside the picture for that just create a SignOut.js component also like this
Components/SignOut.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
import React from 'react' import { auth } from '../firebase.js' import { Button } from '@material-ui/core' function SignOut() { return ( <div style={{ display: 'flex', justifyContent: 'center', position: 'fixed', width: '100%', backgroundColor: '#FAFAFA', top: 0, borderBottom: 'solid 1px lightgray', zIndex: '10' }}> <Button style={{ padding: '20px', fontSize: '15px', borderRadius: '0', fontWeight: '600' }} onClick={() => auth.signOut()}>Sign Out</Button> </div> ) } export default SignOut |
Here for the signOut process we are justing the auth.signOut() method which is provided by firebase to actually signout a user.