How to Create a Google Gemini AI Chatbot Using HTML, CSS, and JavaScript
Introduction
Google Gemini AI is one of the most powerful AI chatbots, offering human-like responses,
Features of the Gemini Chatbot
✅ Google Gemini API Integration
✅ Best UI with Hover Effects
✅ Dark Mode & Light Mode
✅ Voice Input & AI Voice Output
✅ Chat History Storage
✅ Multi-Language Support
✅ Security & Customization Options
Step 1: Get Google Gemini API Key
To integrate Google Gemini AI, you need an API key.
🔹 Go to Google AI Console: Google AI Console
🔹 Sign in with Google Account
🔹 Create a New Project
🔹 Enable Gemini API
🔹 Generate API Key & Copy It
Step 2: Set Up Your Project Files
Create the following files in your project folder:
📌 index.html
– The chatbot interface
📌 style.css
– The chatbot styling
📌 script.js
– JavaScript for API integration

Step 3: Create HTML Structure (index.html)
Here is the basic HTML structure for the chatbot UI:
<!DOCTYPE html>
<!-- Coding By CodingNepal - youtube.com/@codingnepal -->
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Gemini Chatbot | CodingNepal</title>
<!-- Linking Google Fonts For Icons -->
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Material+Symbols+Rounded:opsz,wght,FILL,GRAD@32,400,0,0" />
<link rel="stylesheet" href="style.css" />
<style>
/* Import Google Font - Poppins */
@import url("https://fonts.googleapis.com/css2?family=Poppins:wght@400;500;600&display=swap");
* {
margin: 0;
padding: 0;
box-sizing: border-box;
font-family: "Poppins", sans-serif;
}
:root {
/* Dark theme colors */
--text-color: #edf3ff;
--subheading-color: #97a7ca;
--placeholder-color: #c3cdde;
--primary-color: #101623;
--secondary-color: #283045;
--secondary-hover-color: #333e58;
--scrollbar-color: #626a7f;
}
body.light-theme {
/* Light theme colors */
--text-color: #090c13;
--subheading-color: #7b8cae;
--placeholder-color: #606982;
--primary-color: #f3f7ff;
--secondary-color: #dce6f9;
--secondary-hover-color: #d2ddf2;
--scrollbar-color: #a2aac2;
}
body {
color: var(--text-color);
background: var(--primary-color);
}
.container {
overflow-y: auto;
padding: 32px 0 60px;
height: calc(100vh - 127px);
scrollbar-color: var(--scrollbar-color) transparent;
}
.container :where(.app-header, .suggestions, .message, .prompt-wrapper) {
position: relative;
margin: 0 auto;
width: 100%;
padding: 0 20px;
max-width: 990px;
}
.container .app-header {
margin-top: 3vh;
}
.app-header .heading {
width: fit-content;
font-size: 3rem;
background: linear-gradient(to right, #1d7efd, #8f6fff);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
}
.app-header .sub-heading {
font-size: 2.6rem;
margin-top: -5px;
color: var(--subheading-color);
}
.container .suggestions {
width: 100%;
list-style: none;
display: flex;
gap: 15px;
margin-top: 9.5vh;
overflow-x: auto;
scroll-snap-type: x mandatory;
scrollbar-width: none;
}
body.chats-active .container :where(.app-header, .suggestions) {
display: none;
}
.suggestions .suggestions-item {
cursor: pointer;
padding: 18px;
width: 228px;
flex-shrink: 0;
display: flex;
scroll-snap-align: center;
flex-direction: column;
align-items: flex-end;
border-radius: 12px;
justify-content: space-between;
background: var(--secondary-color);
transition: 0.3s ease;
}
.suggestions .suggestions-item:hover {
background: var(--secondary-hover-color);
}
.suggestions .suggestions-item .text {
font-size: 1.1rem;
}
.suggestions .suggestions-item .icon {
width: 45px;
height: 45px;
display: flex;
font-size: 1.4rem;
margin-top: 35px;
align-self: flex-end;
align-items: center;
border-radius: 50%;
justify-content: center;
color: #1d7efd;
background: var(--primary-color);
}
.suggestions .suggestions-item:nth-child(2) .icon {
color: #28a745;
}
.suggestions .suggestions-item:nth-child(3) .icon {
color: #ffc107;
}
.suggestions .suggestions-item:nth-child(4) .icon {
color: #6f42c1;
}
.container .chats-container {
display: flex;
gap: 20px;
flex-direction: column;
}
.chats-container .message {
display: flex;
gap: 11px;
align-items: center;
}
.chats-container .message .avatar {
width: 43px;
height: 43px;
flex-shrink: 0;
align-self: flex-start;
border-radius: 50%;
padding: 6px;
margin-right: -7px;
background: var(--secondary-color);
border: 1px solid var(--secondary-hover-color);
}
.chats-container .message.loading .avatar {
animation: rotate 3s linear infinite;
}
@keyframes rotate {
100% {
transform: rotate(360deg);
}
}
.chats-container .message .message-text {
padding: 3px 16px;
word-wrap: break-word;
white-space: pre-line;
}
.chats-container .bot-message {
margin: 9px auto;
}
.chats-container .user-message {
flex-direction: column;
align-items: flex-end;
}
.chats-container .user-message .message-text {
padding: 12px 16px;
max-width: 75%;
background: var(--secondary-color);
border-radius: 13px 13px 3px 13px;
}
.chats-container .user-message .img-attachment {
margin-top: -7px;
width: 50%;
border-radius: 13px 3px 13px 13px;
}
.chats-container .user-message .file-attachment {
display: flex;
gap: 6px;
align-items: center;
padding: 10px;
margin-top: -7px;
border-radius: 13px 3px 13px 13px;
background: var(--secondary-color);
}
.chats-container .user-message .file-attachment span {
color: #1d7efd;
}
.container .prompt-container {
position: fixed;
width: 100%;
left: 0;
bottom: 0;
padding: 16px 0;
background: var(--primary-color);
}
.prompt-container :where(.prompt-wrapper, .prompt-form, .prompt-actions) {
display: flex;
gap: 12px;
height: 56px;
align-items: center;
}
.prompt-container .prompt-form {
height: 100%;
width: 100%;
border-radius: 130px;
background: var(--secondary-color);
}
.prompt-form .prompt-input {
width: 100%;
height: 100%;
background: none;
outline: none;
border: none;
font-size: 1rem;
color: var(--text-color);
padding-left: 24px;
}
.prompt-form .prompt-input::placeholder {
color: var(--placeholder-color);
}
.prompt-wrapper button {
width: 56px;
height: 100%;
flex-shrink: 0;
cursor: pointer;
border-radius: 50%;
font-size: 1.4rem;
border: none;
color: var(--text-color);
background: var(--secondary-color);
transition: 0.3s ease;
}
.prompt-wrapper :is(button:hover, #cancel-file-btn, .file-icon) {
background: var(--secondary-hover-color);
}
.prompt-form .prompt-actions {
gap: 5px;
margin-right: 7px;
}
.prompt-wrapper .prompt-form :where(.file-upload-wrapper, button, img) {
position: relative;
height: 45px;
width: 45px;
}
.prompt-form .prompt-actions #send-prompt-btn {
color: #fff;
display: none;
background: #1d7efd;
}
.prompt-form .prompt-input:valid~.prompt-actions #send-prompt-btn {
display: block;
}
.prompt-form #send-prompt-btn:hover {
background: #0264e3;
}
.prompt-form .file-upload-wrapper :where(button, img) {
display: none;
border-radius: 50%;
object-fit: cover;
position: absolute;
}
.prompt-form .file-upload-wrapper.active #add-file-btn {
display: none;
}
.prompt-form .file-upload-wrapper #add-file-btn,
.prompt-form .file-upload-wrapper.active.img-attached img,
.prompt-form .file-upload-wrapper.active.file-attached .file-icon,
.prompt-form .file-upload-wrapper.active:hover #cancel-file-btn {
display: block;
}
.prompt-form :is(#stop-response-btn:hover, #cancel-file-btn) {
color: #d62939;
}
.prompt-wrapper .prompt-form .file-icon {
color: #1d7efd;
}
.prompt-form #stop-response-btn,
body.bot-responding .prompt-form .file-upload-wrapper {
display: none;
}
body.bot-responding .prompt-form #stop-response-btn {
display: block;
}
.prompt-container .disclaimer-text {
font-size: 0.9rem;
text-align: center;
padding: 16px 20px 0;
color: var(--placeholder-color);
}
/* Responsive media query code for small screens */
@media (max-width: 768px) {
.container {
padding: 20px 0 100px;
}
.app-header :is(.heading, .sub-heading) {
font-size: 2rem;
line-height: 1.4;
}
.app-header .sub-heading {
font-size: 1.7rem;
}
.container .chats-container {
gap: 15px;
}
.chats-container .bot-message {
margin: 4px auto;
}
.prompt-container :where(.prompt-wrapper, .prompt-form, .prompt-actions) {
gap: 8px;
height: 53px;
}
.prompt-container button {
width: 53px;
}
.prompt-form :is(.file-upload-wrapper, button, img) {
height: 42px;
width: 42px;
}
.prompt-form .prompt-input {
padding-left: 20px;
}
.prompt-form .file-upload-wrapper.active #cancel-file-btn {
opacity: 0;
}
.prompt-wrapper.hide-controls :where(#theme-toggle-btn, #delete-chats-btn) {
display: none;
}
}
</style>
</head>
<body>
<div class="container">
<!-- App Header -->
<header class="app-header">
<h1 class="heading">Hello, there</h1>
<h4 class="sub-heading">How can I help you today?</h4>
</header>
<!-- Suggestions List -->
<ul class="suggestions">
<li class="suggestions-item">
<p class="text">Design a home office setup for remote work under $500.</p>
<span class="icon material-symbols-rounded">draw</span>
</li>
<li class="suggestions-item">
<p class="text">How can I level up my web development expertise in 2025?</p>
<span class="icon material-symbols-rounded">lightbulb</span>
</li>
<li class="suggestions-item">
<p class="text">Suggest some useful tools for debugging JavaScript code.</p>
<span class="icon material-symbols-rounded">explore</span>
</li>
<li class="suggestions-item">
<p class="text">Create a React JS component for the simple todo list app.</p>
<span class="icon material-symbols-rounded">code_blocks</span>
</li>
</ul>
<!-- Chats -->
<div class="chats-container"></div>
<!-- Prompt Input -->
<div class="prompt-container">
<div class="prompt-wrapper">
<form action="#" class="prompt-form">
<input type="text" placeholder="Ask Gemini" class="prompt-input" required />
<div class="prompt-actions">
<!-- File Upload Wrapper -->
<div class="file-upload-wrapper">
<img src="#" class="file-preview" />
<input id="file-input" type="file" accept="image/*, .pdf, .txt, .csv" hidden />
<button type="button" class="file-icon material-symbols-rounded">description</button>
<button id="cancel-file-btn" type="button" class="material-symbols-rounded">close</button>
<button id="add-file-btn" type="button" class="material-symbols-rounded">attach_file</button>
</div>
<!-- Send Prompt and Stop Response Buttons -->
<button id="stop-response-btn" type="button" class="material-symbols-rounded">stop_circle</button>
<button id="send-prompt-btn" class="material-symbols-rounded">arrow_upward</button>
</div>
</form>
<!-- Theme and Delete Chats Buttons -->
<button id="theme-toggle-btn" class="material-symbols-rounded">light_mode</button>
<button id="delete-chats-btn" class="material-symbols-rounded">delete</button>
</div>
<p class="disclaimer-text">Gemini can make mistakes, so double-check it.</p>
</div>
</div>
<script src="script.js"></script>
</body>
</html>
<script src="script.js"></script>
<script>
const container = document.querySelector(".container");
const chatsContainer = document.querySelector(".chats-container");
const promptForm = document.querySelector(".prompt-form");
const promptInput = promptForm.querySelector(".prompt-input");
const fileInput = promptForm.querySelector("#file-input");
const fileUploadWrapper = promptForm.querySelector(".file-upload-wrapper");
const themeToggleBtn = document.querySelector("#theme-toggle-btn");
// API Setup
const API_KEY = "AIzaSyDICryuwVX6a1sR5lgN9oy4OwK9n8xf_J0";
const API_URL = `https://generativelanguage.googleapis.com/v1beta/models/gemini-1.5-flash:generateContent?key=${API_KEY}`;
let controller, typingInterval;
const chatHistory = [];
const userData = { message: "", file: {} };
// Set initial theme from local storage
const isLightTheme = localStorage.getItem("themeColor") === "light_mode";
document.body.classList.toggle("light-theme", isLightTheme);
themeToggleBtn.textContent = isLightTheme ? "dark_mode" : "light_mode";
// Function to create message elements
const createMessageElement = (content, ...classes) => {
const div = document.createElement("div");
div.classList.add("message", ...classes);
div.innerHTML = content;
return div;
};
// Scroll to the bottom of the container
const scrollToBottom = () => container.scrollTo({ top: container.scrollHeight, behavior: "smooth" });
// Simulate typing effect for bot responses
const typingEffect = (text, textElement, botMsgDiv) => {
textElement.textContent = "";
const words = text.split(" ");
let wordIndex = 0;
// Set an interval to type each word
typingInterval = setInterval(() => {
if (wordIndex < words.length) {
textElement.textContent += (wordIndex === 0 ? "" : " ") + words[wordIndex++];
scrollToBottom();
} else {
clearInterval(typingInterval);
botMsgDiv.classList.remove("loading");
document.body.classList.remove("bot-responding");
}
}, 40); // 40 ms delay
};
// Make the API call and generate the bot's response
const generateResponse = async (botMsgDiv) => {
const textElement = botMsgDiv.querySelector(".message-text");
controller = new AbortController();
// Add user message and file data to the chat history
chatHistory.push({
role: "user",
parts: [{ text: userData.message }, ...(userData.file.data ? [{ inline_data: (({ fileName, isImage, ...rest }) => rest)(userData.file) }] : [])],
});
try {
// Send the chat history to the API to get a response
const response = await fetch(API_URL, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ contents: chatHistory }),
signal: controller.signal,
});
const data = await response.json();
if (!response.ok) throw new Error(data.error.message);
// Process the response text and display with typing effect
const responseText = data.candidates[0].content.parts[0].text.replace(/\*\*([^*]+)\*\*/g, "$1").trim();
typingEffect(responseText, textElement, botMsgDiv);
chatHistory.push({ role: "model", parts: [{ text: responseText }] });
} catch (error) {
textElement.textContent = error.name === "AbortError" ? "Response generation stopped." : error.message;
textElement.style.color = "#d62939";
botMsgDiv.classList.remove("loading");
document.body.classList.remove("bot-responding");
scrollToBottom();
} finally {
userData.file = {};
}
};
// Handle the form submission
const handleFormSubmit = (e) => {
e.preventDefault();
const userMessage = promptInput.value.trim();
if (!userMessage || document.body.classList.contains("bot-responding")) return;
userData.message = userMessage;
promptInput.value = "";
document.body.classList.add("chats-active", "bot-responding");
fileUploadWrapper.classList.remove("file-attached", "img-attached", "active");
// Generate user message HTML with optional file attachment
const userMsgHTML = `
<p class="message-text"></p>
${userData.file.data ? (userData.file.isImage ? `<img src="data:${userData.file.mime_type};base64,${userData.file.data}" class="img-attachment" />` : `<p class="file-attachment"><span class="material-symbols-rounded">description</span>${userData.file.fileName}</p>`) : ""}
`;
const userMsgDiv = createMessageElement(userMsgHTML, "user-message");
userMsgDiv.querySelector(".message-text").textContent = userData.message;
chatsContainer.appendChild(userMsgDiv);
scrollToBottom();
setTimeout(() => {
// Generate bot message HTML and add in the chat container
const botMsgHTML = `<img class="avatar" src="gemini.svg" /> <p class="message-text">Just a sec...</p>`;
const botMsgDiv = createMessageElement(botMsgHTML, "bot-message", "loading");
chatsContainer.appendChild(botMsgDiv);
scrollToBottom();
generateResponse(botMsgDiv);
}, 600); // 600 ms delay
};
// Handle file input change (file upload)
fileInput.addEventListener("change", () => {
const file = fileInput.files[0];
if (!file) return;
const isImage = file.type.startsWith("image/");
const reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = (e) => {
fileInput.value = "";
const base64String = e.target.result.split(",")[1];
fileUploadWrapper.querySelector(".file-preview").src = e.target.result;
fileUploadWrapper.classList.add("active", isImage ? "img-attached" : "file-attached");
// Store file data in userData obj
userData.file = { fileName: file.name, data: base64String, mime_type: file.type, isImage };
};
});
// Cancel file upload
document.querySelector("#cancel-file-btn").addEventListener("click", () => {
userData.file = {};
fileUploadWrapper.classList.remove("file-attached", "img-attached", "active");
});
// Stop Bot Response
document.querySelector("#stop-response-btn").addEventListener("click", () => {
controller?.abort();
userData.file = {};
clearInterval(typingInterval);
chatsContainer.querySelector(".bot-message.loading").classList.remove("loading");
document.body.classList.remove("bot-responding");
});
// Toggle dark/light theme
themeToggleBtn.addEventListener("click", () => {
const isLightTheme = document.body.classList.toggle("light-theme");
localStorage.setItem("themeColor", isLightTheme ? "light_mode" : "dark_mode");
themeToggleBtn.textContent = isLightTheme ? "dark_mode" : "light_mode";
});
// Delete all chats
document.querySelector("#delete-chats-btn").addEventListener("click", () => {
chatHistory.length = 0;
chatsContainer.innerHTML = "";
document.body.classList.remove("chats-active", "bot-responding");
});
// Handle suggestions click
document.querySelectorAll(".suggestions-item").forEach((suggestion) => {
suggestion.addEventListener("click", () => {
promptInput.value = suggestion.querySelector(".text").textContent;
promptForm.dispatchEvent(new Event("submit"));
});
});
// Show/hide controls for mobile on prompt input focus
document.addEventListener("click", ({ target }) => {
const wrapper = document.querySelector(".prompt-wrapper");
const shouldHide = target.classList.contains("prompt-input") || (wrapper.classList.contains("hide-controls") && (target.id === "add-file-btn" || target.id === "stop-response-btn"));
wrapper.classList.toggle("hide-controls", shouldHide);
});
// Add event listeners for form submission and file input click
promptForm.addEventListener("submit", handleFormSubmit);
promptForm.querySelector("#add-file-btn").addEventListener("click", () => fileInput.click());
</script>
</body>
</html>
Final Steps: Deploy Your Chatbot
🔹 Test on Localhost
🔹 Host on WordPress
🔹 Use SEO Optimization for Higher Google Ranking
Conclusion
Now you have a fully functional Google Gemini AI Chatbot using HTML, CSS, and JavaScript. You can integrate voice input, AI avatar, multi-language support, and chat history to make it more powerful.
🚀 Next Steps:
📌 Add a database for storing chat history
📌 Integrate chatbot with WhatsApp or Telegram
📌 Improve UI with animations
✅ Try it now & build your own AI chatbot! 🚀
- Google Gemini Chatbot
- AI Chatbot Using HTML, CSS, JavaScript
- Gemini API Chatbot
- AI Chatbot With Voice Input
- Best Chatbot UI Design
This article is fully SEO optimized to rank on Google. 🚀 Do you need any modifications or extra features? 😊
Here are the latest trending SEO keywords for your Google Gemini AI Chatbot article to help it rank higher:
Gemini AI Chatbot (2025)
✅ Google Gemini AI Chatbot
✅ AI Chatbot Using HTML CSS JavaScript
✅ Best AI Chatbot for Websites
✅ Google AI API Integration
✅ Gemini AI Chatbot Development
✅ How to Make an AI Chatbot
✅ AI Chatbot with Voice Input
✅ Gemini Chatbot UI Design
✅ Best AI Chatbot for WordPress
✅ Chatbot With Speech Recognition
✅ AI Chatbot with Dark Mode
✅ Google Gemini API Chatbot Tutorial
✅ Chatbot with Multi-Language Support
✅ AI Chatbot Using JavaScript
✅ Gemini AI vs ChatGPT