Welcome to my website U-GINE MEDIA. In this website I teach tutorials and share source code on some programming (HTML, CSS, JavaScript, React) tutorials.
Before we get started, do well to subscribe to my channel to never miss out on any update that I post every single day.
You can also visit my GitHub where I upload all the code I have.
In this tutorial topic "Build AI ChatBot App in ReactJS | ChatBot Like ChatGPT & Gemini AI", we will learn how to create it following these simple easy steps.
Share this to your programmer friends. If y'all learnt something today, make sure to let me in the comments.
Get Source Code.
If interested in watching the video before running the code below, you can click the play button to get started.
Setting Up the Project
Create a Project Folder:
- Make a new folder, for instance, “”.
- Open this folder in your VS Code editor.
Initialize the Project:
npm create vite@latest ./ -- --template react
npm install
npm run dev
Modify folder and CSS Files:
- Remove the default assets folder and App.css file.
- Replace the content of index.css with the provided CSS code.
@import url("https://fonts.googleapis.com/css2?family=Poppins:wght@400;500;600&display=swap");
/* Global Styles */
* {
margin: 0;
padding: 0;
box-sizing: border-box;
font-family: "Poppins", sans-serif;
}
body {
background: #f3f4f6; /* Soft gray background */
}
.chat-container {
display: flex;
height: 100vh;
}
/* Sidebar */
.sidebar {
width: 250px;
background: linear-gradient(-90deg, #6366f1, #4338ca); /* Purple gradient */
color: #fff;
display: flex;
flex-direction: column;
justify-content: space-between;
}
.logo {
margin: 20px;
font-size: 1.5rem;
margin-bottom: 2rem;
font-weight: 600;
display: flex;
align-items: center;
gap: 0.2em;
}
.logo img {
width: 35px;
}
.logo span {
background: linear-gradient(to right, #cf94e9, #61bcd5);
-webkit-background-clip: text;
background-clip: text;
color: transparent;
}
.channels {
flex: 1;
margin: 20px;
}
.channel {
margin: 10px 0;
cursor: pointer;
opacity: 0.8;
padding: 10px 20px;
border-radius: 8px;
transition: 0.3s ease;
}
.channel:hover {
opacity: 0.5;
color: #111827;
background: #f9fafb;
}
.channel.active {
font-weight: 600;
opacity: 1;
color: #111827;
background: #f9fafb;
}
.user-profile {
display: flex;
align-items: center;
gap: 0.4em;
padding: 10px 20px;
margin: 20px;
cursor: pointer;
border-radius: 8px;
transition: 0.5s ease;
}
.avatar {
filter: invert(100%);
transition: 0.3s ease;
}
.username {
font-weight: 500;
font-size: 1.1em;
}
.user-profile:hover {
color: #111827;
background: #f9fafb;
}
.user-profile:hover .avatar {
filter: none;
}
/* Main Chat Area */
.chat-main {
flex: 1;
display: flex;
flex-direction: column;
background: #fff;
box-shadow: -2px 0 10px rgba(0, 0, 0, 0.05);
}
/* Chat Header */
.chat-header {
padding: 20px;
border-bottom: 1px solid #e5e7eb;
background-color: #f9fafb;
}
.chat-header h2 {
margin: 0;
font-size: 1.4rem;
}
.topic {
margin: 5px 0 0;
font-size: 0.9rem;
color: #6b7280;
}
/* Chat Messages */
.chat-messages {
position: relative;
flex: 1;
padding: 20px;
overflow-y: auto;
background: #f3f4f6;
}
.message-bubble {
max-width: 60%;
padding: 10px 15px;
margin-bottom: 15px;
border-radius: 10px;
line-height: 1.4;
}
.my-message {
margin-left: auto;
background: linear-gradient(135deg, #6366f1, #4338ca);
color: #fff;
text-align: right;
}
.other-message {
background-color: #e2e8f0;
color: #111827;
}
.message-user {
font-weight: 600;
margin-bottom: 5px;
}
.message-text {
margin: 0;
}
.chat-messages button {
position: sticky;
bottom: 10px;
left: 50%;
transform: translateX(-50%);
background: transparent;
border: none;
width: 40px;
height: 40px;
background: #979da8;
border-radius: 50%;
cursor: pointer;
display: none;
justify-content: center;
align-items: center;
}
.chat-messages button img {
width: 35px;
filter: invert(100%);
}
/* Chat Input */
.chat-input {
display: flex;
padding: 10px 20px;
border-top: 1px solid #e5e7eb;
background-color: #f9fafb;
}
.chat-input input {
flex: 1;
border: 1px solid #d1d5db;
border-radius: 8px;
padding: 12px 10px;
outline: none;
margin-right: 10px;
font-size: 0.9em;
}
.chat-input button[disabled] {
background: gray;
opacity: 0.4;
cursor: not-allowed;
}
.chat-input button {
background: linear-gradient(135deg, #6366f1, #4338ca);
border: none;
color: #fff;
padding: 10px 20px;
border-radius: 8px;
cursor: pointer;
transition: background 0.3s ease;
}
.footer-message {
text-align: center;
font-size: 0.8em;
padding: 2px 0;
}
Creating the Components
- Chat.jsx
Adding the Codes
import React, { useEffect, useState, useRef } from "react";
import logo from "../assets/logo.png";
import user from "../assets/user.png";
import arrowDownImg from "../assets/arrow-down.png";
const Chat = () => {
// Chat messages (some initial sample messages)
const [messages, setMessages] = useState([]);
//Text Input Value
const [inputValue, setInputValue] = useState("");
//Set Is Loading State
const [loading, setIsLoading] = useState(false);
const handleAIResponse = async (userMessage) => {
setIsLoading(true);
try {
//Replace Key with your own API Key
const response = await fetch(
`https://generativelanguage.googleapis.com/v1beta/models/gemini-1.5-flash:generateContent?key=${
import.meta.env.VITE_API_KEY
}`,
{
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
contents: [
{
parts: [
{
text: `${userMessage}`,
},
],
},
],
}),
}
);
const data = await response.json();
// Hypothetical AI-generated text from the Gemini API
const aiMessage =
data.candidates[0].content.parts[0].text ??
"Sorry, I couldn't process that.";
let index = 0;
let tempText = "";
const interval = setInterval(() => {
tempText += aiMessage.charAt(index);
setMessages((prev) => {
const newMessages = [...prev];
if (newMessages[newMessages.length - 1]?.type === "bot") {
newMessages[newMessages.length - 1].text = tempText;
} else {
newMessages.push({
id: prev.length + 1,
type: "bot",
user: "SkylineGPT",
text: tempText,
});
}
return newMessages;
});
index++;
if (index >= aiMessage.length) clearInterval(interval);
}, 10);
} catch (error) {
setMessages((prev) => [
...prev,
{
id: prev.length + 1,
type: "bot",
user: "SkylineGPT",
text: "Oops! Something went wrong.",
},
]);
} finally {
setIsLoading(false);
}
};
//Send message and get AI response;
const handleSend = async () => {
if (inputValue.trim() === "") return;
const newMessage = {
id: messages.length + 1,
type: "user",
user: "Me",
text: inputValue,
};
//Add user message to chat;
setMessages((prev) => [...prev, newMessage]);
setInputValue("");
//Call AI response function
await handleAIResponse(inputValue);
};
//Make a reference to the chat Message
const chatMessages = useRef(null);
// Scrolls the chat container to the bottom smoothly.
// This is useful for keeping the latest messages in view
// when a new message is added.
const handleChatScroll = () => {
chatMessages.current.scrollTo({
top: chatMessages.current.scrollHeight,
behavior: "smooth",
});
};
useEffect(() => {
// Adds a scroll event listener to the chatMessages container.
// When the user scrolls, it displays the "arrowDown" element,
// which could be used as a scroll-to-bottom indicator.
if (chatMessages.current) {
chatMessages.current.addEventListener("scroll", (e) => {
document.getElementById("arrowDown").style.display = "flex";
});
}
}, []);
return (
<div className="chat-container">
{/* Sidebar */}
<aside className="sidebar">
<h2 className="logo">
<img src={logo} alt="logo" /> <span>SkylineGPT</span>
</h2>
<div className="channels">
<p className="channel active">General</p>
<p className="channel">Tech Talk</p>
<p className="channel">Design</p>
</div>
<div className="user-profile">
<img src={user} alt="User Avatar" className="avatar" />
<span className="username">You</span>
</div>
</aside>
{/* Main Chat Area */}
<main className="chat-main">
<header className="chat-header">
<h2>General</h2>
<p className="topic">Chat about anything and everything!</p>
</header>
<section className="chat-messages" ref={chatMessages}>
{messages.map((msg) => (
<div
key={msg.id}
className={`message-bubble ${
msg.user === "Me" ? "my-message" : "other-message"
}`}
>
<p className="message-user">{msg.user}</p>
<p className="message-text">{msg.text}</p>
</div>
))}
{loading && (
<div className="message-bubble other-message">
<p className="message-user">SkylineGPT</p>
<p className="message-text">Generating Response...</p>
</div>
)}
<button id="arrowDown" onClick={handleChatScroll}>
<img src={arrowDownImg} alt="" />
</button>
</section>
<footer className="chat-input">
<input
type="text"
placeholder="Type your message..."
value={inputValue}
onChange={(e) => setInputValue(e.target.value)}
onKeyDown={(e) => e.key === "Enter" && handleSend()}
/>
<button disabled={!inputValue.trim()} onClick={handleSend}>
Send
</button>
</footer>
<p className="footer-message">
SkylineGPT can make mistakes. Check important info.
</p>
</main>
</div>
);
};
export default Chat;
import React from "react";
import Chat from "./components/Chat";
const App = () => {
return <Chat />;
};
export default App;
0 Comments
We are happy to hear from you.