Build A Realtime Chat App With Supabase And React
Build a Realtime Chat App with Supabase and React: A Comprehensive Guide
Hey everyone! š Ever wanted to build your own realtime chat application ? Something like a mini-Slack or Discord, where messages pop up instantly? Well, youāre in luck! This guide will walk you through building just that using Supabase and React . Supabase provides a fantastic backend, complete with a realtime database, authentication, and more, while React lets us build the slick, interactive frontend. Weāll cover everything from setting up your Supabase project to displaying messages in your chat window. So, grab your favorite coding beverage, and letās dive in! š
Table of Contents
Setting up Your Supabase Project
Alright, first things first, letās get our backend ready. Weāll be using Supabase , so if you havenāt already, head over to Supabase and create an account. Itās free to get started, and they have generous free-tier limits, perfect for learning and small projects. Once youāre logged in, create a new project. Give it a name (something like āreact-chat-appā is a good start), choose a region close to you, and set up a database password. Supabase will then provision your project, which might take a few minutes. ā±ļø
After your project is created, youāll land on your project dashboard. Hereās where the magic happens! We need to set up a table in the Supabase database to store our chat messages. In the left-hand navigation, click on the āTable Editorā (it looks like a database icon). Then, click āNew tableā.
Letās create a table called
messages
. Weāll need a few columns:
-
id: This will be the primary key (Supabase will auto-generate this for us). -
created_at: This will store the timestamp of when the message was created (Supabase will also handle this). -
user_id: This will be a foreign key referencing the user who sent the message. Weāll use Supabaseās built-in authentication for this. -
content: This will store the actual message text (aTEXTdata type).
Go ahead and add these columns to your
messages
table. Make sure to set the
id
column as the primary key and the
created_at
column to be automatically generated. Now, letās enable
realtime
for this table. In the Table Editor, click on the āRealtimeā tab and enable the realtime feature for the
messages
table. This is super important because it allows Supabase to push updates to our React app whenever a new message is added or updated. Boom! Instant updates. š„
Finally, grab your projectās API keys. Youāll find these in the āSettingsā -> āAPIā section of your Supabase dashboard. Youāll need the
anon public
key and the
service_role
key (for server-side operations, we wonāt need this in this tutorial, but itās good to know). Keep these keys safe and secure, especially the service role key! Weāll use these keys in our React application to connect to our Supabase backend. With your Supabase project set up, we can now move on to the fun part: building the React frontend!
Creating the React Frontend
Okay, time to switch gears and start building the frontend of our chat app with React . If you donāt have Node.js and npm/yarn installed, go ahead and install them. Then, open your terminal and letās create a new React app using Create React App (CRA).
npx create-react-app react-chat-app
cd react-chat-app
This will set up a basic React project for us. Now, letās install the necessary dependencies. Weāll need the
@supabase/supabase-js
library to interact with our Supabase backend and a UI library (like Material UI, Ant Design, or even just plain CSS) for styling. For this example, letās use a very basic setup and style with some simple CSS.
npm install @supabase/supabase-js
Once the installation is complete, open your project in your favorite code editor (VS Code, Sublime Text, etc.). Letās clean up the
src
directory a bit. Delete the files you wonāt need, like
App.css
,
App.test.js
,
logo.svg
, and any other files you donāt plan to use. Then, letās start by modifying the
App.js
file.
First, weāll initialize the Supabase client. In
App.js
, import
createClient
from
@supabase/supabase-js
and initialize the client using your Supabase project URL and anon key, which you obtained earlier from your Supabase dashboard. It should look something like this:
import { createClient } from '@supabase/supabase-js';
const supabaseUrl = 'YOUR_SUPABASE_URL';
const supabaseAnonKey = 'YOUR_SUPABASE_ANON_KEY';
const supabase = createClient(supabaseUrl, supabaseAnonKey);
function App() {
// ... rest of the code
}
export default App;
Remember to replace
YOUR_SUPABASE_URL
and
YOUR_SUPABASE_ANON_KEY
with your actual Supabase project credentials. Next, letās set up the basic UI for our chat app. Weāll need a container for the chat messages and an input field with a button to send messages. Hereās a basic example:
import React, { useState, useEffect } from 'react';
import { createClient } from '@supabase/supabase-js';
import './App.css'; // Import your CSS file
const supabaseUrl = 'YOUR_SUPABASE_URL';
const supabaseAnonKey = 'YOUR_SUPABASE_ANON_KEY';
const supabase = createClient(supabaseUrl, supabaseAnonKey);
function App() {
const [messages, setMessages] = useState([]);
const [newMessage, setNewMessage] = useState('');
// ... rest of the code
return (
<div className="chat-container">
<div className="chat-messages">
{messages.map(message => (
<div key={message.id} className="message">
{message.content}
</div>
))}
</div>
<div className="chat-input">
<input
type="text"
value={newMessage}
onChange={e => setNewMessage(e.target.value)}
placeholder="Type your message..."
/>
<button onClick={handleSendMessage}>Send</button>
</div>
</div>
);
}
export default App;
In this example, we have a
chat-container
, a
chat-messages
section where messages will be displayed, and a
chat-input
section with a text input and a send button. We also have two state variables:
messages
to hold the chat messages and
newMessage
to hold the text the user is currently typing. This is a very basic structure, but it sets the foundation for our chat appās interface. Feel free to customize the styling and structure to fit your design preferences. Remember to create an
App.css
file and add some basic CSS rules for the
.chat-container
,
.chat-messages
,
.message
, and
.chat-input
classes to make the app look presentable. Style it up, guys! š
Fetching and Displaying Messages
Now, letās fetch the messages from our Supabase database and display them in our React app. Weāll use the
useEffect
hook to fetch messages when the component mounts and listen for real-time updates from Supabase. This ensures that the chat messages are always up-to-date.
First, letās create a function to fetch messages from Supabase. Weāll use the
supabase.from()
method to query the
messages
table and order the messages by their creation time (
created_at
).
async function fetchMessages() {
const { data, error } = await supabase
.from('messages')
.select('*')
.order('created_at', { ascending: true });
if (error) {
console.error('Error fetching messages:', error);
} else {
setMessages(data);
}
}
Inside the
useEffect
hook, call this
fetchMessages
function to fetch the initial set of messages. Then, we need to set up a real-time listener using
supabase.channel()
. This will subscribe to changes in the
messages
table and update the messages state whenever a new message is added or an existing message is updated. This listener is the core of the
realtime
functionality, and itās what makes the chat app truly interactive.
useEffect(() => {
fetchMessages();
const channel = supabase
.channel('public:messages')
.on('INSERT', payload => {
setMessages(prevMessages => [...prevMessages, payload.new]);
})
.subscribe();
return () => {
channel.unsubscribe(); // Unsubscribe when the component unmounts
};
}, []);
In the code above, the
on('INSERT', payload => { ... })
function listens for
INSERT
events, which occur when a new message is added to the
messages
table. When a new message arrives, we update the
messages
state by adding the new message to the existing array of messages. This will trigger a re-render of the
chat-messages
section, displaying the new message. The
subscribe()
method starts the subscription, and the
unsubscribe()
method (in the
return
function) stops the subscription when the component unmounts, preventing memory leaks. This is very important for keeping your app clean and performant. š
Now, add a
fetchMessages()
call inside the
useEffect
hook, right before the channel setup to fetch messages on component mount. This will make your app load already existing messages. This is how the message display is handled to make it work. With this in place, your chat app should now fetch and display the existing messages from your database and update the chat in
realtime
as new messages are added. Test it out! Open your app, and then add messages directly to your Supabase database using the Table Editor. Watch as they magically appear in your app! Cool, right?
Sending Messages
Next, letās add the functionality to send messages. Weāll create a function called
handleSendMessage
that will be triggered when the user clicks the