Building A Chat UI With Chat Bubbles In React JS

by StackCamp Team 49 views

Hey guys! 👋 Ever wanted to build a chat interface in your React app, complete with those cool chat bubbles that make conversations feel so natural? You've come to the right place! In this article, we're going to dive deep into how to create a chat-like UI with chat bubbles in React JS. We'll be tackling the challenge of positioning these bubbles dynamically based on the sender, using Material UI for styling, Context API for state management, and JSX for structuring our components. Let's get started and make your chat interface dreams a reality!

Understanding the Basics

Before we jump into the code, let's break down the fundamental concepts we'll be using. React JS, a powerful JavaScript library for building user interfaces, will be our foundation. We'll use Material UI, a popular React UI framework, to make our chat bubbles look polished and professional. JSX, a syntax extension to JavaScript, will help us write our React components in a clear and concise way. The Context API will be crucial for managing our application's state, ensuring that our chat messages are accessible across different components. And finally, CSS will be our go-to tool for styling and positioning the chat bubbles.

Setting Up Your React Environment

First things first, you'll need to set up a React environment. If you're starting from scratch, Create React App is your best friend. It's a tool that sets up a new React project with all the modern build tools you need. To get started, open your terminal and run:

npx create-react-app chat-app
cd chat-app
npm start

This will create a new React project named "chat-app", navigate into the project directory, and start the development server. You should see your app running in your browser at http://localhost:3000.

Installing Material UI

Next, let's install Material UI, our go-to library for beautiful and responsive components. Run the following command in your terminal:

npm install @mui/material @emotion/react @emotion/styled

This command installs Material UI's core library, along with Emotion, a CSS-in-JS library that Material UI uses for styling. With Material UI installed, we're ready to start building our chat interface.

Structuring Your Chat Interface with JSX

Now, let's dive into the heart of our project: structuring the chat interface with JSX. We'll create a few key components to manage our chat layout and message display. Imagine our chat interface as a container holding a list of messages. Each message will be displayed as a bubble, positioned either on the left or right side of the chat window, depending on who sent it. To achieve this, we'll need components for:

  1. ChatContainer: This component will act as the main container for our chat interface, holding the message list and input area.
  2. MessageList: This component will render the list of messages, mapping our message data to individual message components.
  3. MessageBubble: This component will represent a single chat bubble, displaying the message content and positioning it correctly based on the sender.
  4. MessageInput: This component will provide an input field and send button for users to type and send messages.

Creating the ChatContainer Component

Let's start by creating the ChatContainer component. This component will be the main entry point for our chat interface. Create a new file named ChatContainer.js in your src directory and add the following code:

import React from 'react';
import MessageList from './MessageList';
import MessageInput from './MessageInput';
import { Container } from '@mui/material';

function ChatContainer() {
  return (
    <Container maxWidth="md">
      <h1>React Chat UI</h1>
      <MessageList />
      <MessageInput />
    </Container>
  );
}

export default ChatContainer;

Here, we're importing the necessary components and Material UI's Container component to provide a responsive layout. The ChatContainer component renders a heading, the MessageList component (which we'll create next), and the MessageInput component (which we'll also create).

Building the MessageList Component

The MessageList component is responsible for rendering the list of messages. It will receive an array of message objects and map them to individual MessageBubble components. Create a new file named MessageList.js in your src directory and add the following code:

import React, { useContext } from 'react';
import MessageBubble from './MessageBubble';
import { ChatContext } from './ChatContext';

function MessageList() {
  const { messages } = useContext(ChatContext);

  return (
    <div>
      {messages.map((message) => (
        <MessageBubble key={message.id} message={message} />
      ))}
    </div>
  );
}

export default MessageList;

In this component, we're using the useContext hook to access the messages array from our ChatContext (which we'll set up later). We then map over the messages array and render a MessageBubble component for each message. Each MessageBubble receives a message prop containing the message object.

Crafting the MessageBubble Component

The MessageBubble component is the star of the show. It's responsible for displaying a single chat bubble and positioning it on the left or right side based on the sender. Create a new file named MessageBubble.js in your src directory and add the following code:

import React from 'react';
import { Paper, Typography, Box } from '@mui/material';

function MessageBubble({ message }) {
  const isCurrentUser = message.sender === 'currentUser';

  return (
    <Box
      sx={{
        display: 'flex',
        justifyContent: isCurrentUser ? 'flex-end' : 'flex-start',
        mb: 2,
      }}
    >
      <Paper
        sx={{
          p: 1.5,
          borderRadius: isCurrentUser ? '20px 0 20px 20px' : '0 20px 20px 20px',
          bgcolor: isCurrentUser ? '#DCF8C6' : '#FFFFFF',
          maxWidth: '80%',
        }}
      >
        <Typography variant="body1">{message.text}</Typography>
      </Paper>
    </Box>
  );
}

export default MessageBubble;

This component receives a message prop and determines whether the message was sent by the current user. Based on this, it uses Material UI's Paper and Typography components to create a chat bubble with different styles and positioning. The isCurrentUser variable is used to conditionally apply styles, ensuring that messages from the current user are displayed on the right and messages from others are displayed on the left.

Designing the MessageInput Component

Finally, let's create the MessageInput component, which will provide an input field and send button for users to type and send messages. Create a new file named MessageInput.js in your src directory and add the following code:

import React, { useState, useContext } from 'react';
import { TextField, IconButton, InputAdornment } from '@mui/material';
import { Send as SendIcon } from '@mui/icons-material';
import { ChatContext } from './ChatContext';

function MessageInput() {
  const [newMessage, setNewMessage] = useState('');
  const { addMessage } = useContext(ChatContext);

  const handleSendMessage = () => {
    if (newMessage.trim() !== '') {
      addMessage({
        id: Date.now(),
        text: newMessage,
        sender: 'currentUser',
      });
      setNewMessage('');
    }
  };

  return (
    <TextField
      fullWidth
      variant="outlined"
      placeholder="Type a message..."
      value={newMessage}
      onChange={(e) => setNewMessage(e.target.value)}
      onKeyPress={(e) => {
        if (e.key === 'Enter') {
          handleSendMessage();
        }
      }}
      InputProps={{
        endAdornment: (
          <InputAdornment position="end">
            <IconButton onClick={handleSendMessage}>
              <SendIcon />
            </IconButton>
          </InputAdornment>
        ),
      }}
    />
  );
}

export default MessageInput;

This component uses Material UI's TextField and IconButton components to create an input field with a send button. It uses the useState hook to manage the input value and the useContext hook to access the addMessage function from our ChatContext (which we'll set up next). When the user types a message and presses Enter or clicks the send button, the handleSendMessage function is called, which adds the new message to the chat.

Managing State with Context API

Now that we have our components in place, let's set up the Context API to manage our chat state. The Context API allows us to share state between components without having to pass props down manually at every level. This is perfect for our chat application, where we need to share the message list and add new messages from different components.

Creating the ChatContext

Create a new file named ChatContext.js in your src directory and add the following code:

import React, { createContext, useState } from 'react';

export const ChatContext = createContext();

export function ChatProvider({ children }) {
  const [messages, setMessages] = useState([
    {
      id: 1,
      text: 'Hey there! 👋',
      sender: 'otherUser',
    },
    {
      id: 2,
      text: 'Hi! How can I help you?',
      sender: 'currentUser',
    },
  ]);

  const addMessage = (message) => {
    setMessages((prevMessages) => [...prevMessages, message]);
  };

  return (
    <ChatContext.Provider value={{ messages, addMessage }}>
      {children}
    </ChatContext.Provider>
  );
}

Here, we're creating a ChatContext using createContext. We're also creating a ChatProvider component that will wrap our app and provide the chat state to its children. The ChatProvider uses the useState hook to manage the messages array and provides an addMessage function to add new messages to the list. We also have some dummy data here to initiate the chat.

Wrapping Your App with the ChatProvider

Now, let's wrap our app with the ChatProvider to make the chat state available to all components. Open your src/index.js file and modify it as follows:

import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import { ChatProvider } from './ChatContext';

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <React.StrictMode>
    <ChatProvider>
      <App />
    </ChatProvider>
  </React.StrictMode>
);

We're importing the ChatProvider and wrapping our App component with it. This makes the messages array and addMessage function available to all components within our app.

Connecting Components to the ChatContext

With the ChatContext set up, we can now connect our components to it. We've already done this in the MessageList and MessageInput components using the useContext hook. These components can now access the messages array and addMessage function from the ChatContext.

Styling Your Chat Bubbles with CSS and Material UI

Styling is key to making our chat bubbles look great. We've already used Material UI's styling capabilities within our MessageBubble component to control the bubble's appearance and positioning. Let's recap how we did this and explore some additional styling options.

Conditional Styling

The most important aspect of styling our chat bubbles is the ability to conditionally apply styles based on the sender. We achieve this using the isCurrentUser variable in the MessageBubble component. This variable determines whether the message was sent by the current user and allows us to apply different styles accordingly.

For example, we use the isCurrentUser variable to control the background color and border radius of the chat bubbles:

<Paper
  sx={{
    p: 1.5,
    borderRadius: isCurrentUser ? '20px 0 20px 20px' : '0 20px 20px 20px',
    bgcolor: isCurrentUser ? '#DCF8C6' : '#FFFFFF',
    maxWidth: '80%',
  }}
>
  <Typography variant="body1">{message.text}</Typography>
</Paper>

Here, we're using the sx prop, which is Material UI's way of applying styles using CSS-in-JS. We're setting the borderRadius and bgcolor based on the isCurrentUser variable, creating the effect of messages from the current user appearing on the right with a green background and messages from others appearing on the left with a white background.

Additional Styling Options

Material UI provides a wide range of styling options that you can use to customize your chat bubbles further. You can control the padding, margin, font size, color, and many other aspects of the bubble's appearance.

For example, you can add a shadow to the bubbles to make them stand out more:

<Paper
  sx={{
    p: 1.5,
    borderRadius: isCurrentUser ? '20px 0 20px 20px' : '0 20px 20px 20px',
    bgcolor: isCurrentUser ? '#DCF8C6' : '#FFFFFF',
    maxWidth: '80%',
    boxShadow: '0px 3px 5px rgba(0, 0, 0, 0.2)',
  }}
>
  <Typography variant="body1">{message.text}</Typography>
</Paper>

Here, we're adding a boxShadow property to the sx object, which will add a subtle shadow to the chat bubbles.

Handling Dynamic Bubble Placement

The key to creating a chat-like UI is the dynamic placement of chat bubbles. We want messages from the current user to appear on the right and messages from others to appear on the left. We've already achieved this using the isCurrentUser variable and conditional styling in the MessageBubble component.

Flexbox for Positioning

We're using Flexbox to control the positioning of our chat bubbles. Flexbox is a powerful CSS layout module that makes it easy to align and distribute elements within a container. In our case, we're using Flexbox to align the chat bubbles to the left or right side of the chat window.

In the MessageBubble component, we have the following code:

<Box
  sx={{
    display: 'flex',
    justifyContent: isCurrentUser ? 'flex-end' : 'flex-start',
    mb: 2,
  }}
>
  {/* ... */}
</Box>

Here, we're using a Box component (which is a Material UI component that renders a div with additional styling capabilities) with the sx prop to apply Flexbox styles. We're setting the display property to flex to enable Flexbox layout. The justifyContent property is used to control the horizontal alignment of the chat bubble. We're setting it to flex-end if the message is from the current user (aligning the bubble to the right) and flex-start if the message is from someone else (aligning the bubble to the left).

Ensuring Correct Bubble Order

To ensure that our chat bubbles are displayed in the correct order, we need to make sure that the messages are sorted chronologically. In our ChatContext, we're simply adding new messages to the end of the messages array. This ensures that the messages are displayed in the order they were sent.

Conclusion: Your Chat UI is Ready!

Woohoo! 🎉 You've successfully built a chat-like UI with chat bubbles in React JS! We've covered a lot of ground, from setting up your React environment and installing Material UI to structuring your components with JSX, managing state with the Context API, and styling your chat bubbles with CSS. You've learned how to dynamically position chat bubbles based on the sender and ensure that messages are displayed in the correct order.

Key Takeaways

  • React JS is a powerful library for building user interfaces.
  • Material UI provides beautiful and responsive components for your React apps.
  • JSX makes it easy to structure your React components.
  • The Context API is crucial for managing state in React applications.
  • CSS and Flexbox are essential for styling and positioning elements.

Next Steps

Now that you have a basic chat UI, you can explore more advanced features, such as:

  • Real-time messaging: Integrate a real-time messaging service like Socket.IO or Firebase to enable real-time communication.
  • User authentication: Add user authentication to allow users to log in and send messages as themselves.
  • Message history: Implement a system to store and retrieve message history.
  • File uploads: Allow users to send files and images in the chat.
  • Typing indicators: Display a typing indicator when a user is typing a message.

Keep experimenting and building, and you'll become a chat UI master in no time! Happy coding! 🚀