Chatbots are transforming how we interact with technology, making it easier to access information and services in seconds. Imagine creating your own AI chatbot that can answer any question instantly. Whether for fun, to add interactivity to your website, or to provide a helpful tool for users, building a chatbot using HTML, CSS, and JavaScript is simpler than you might think.
This guide will walk you through the steps to build an AI-powered chatbot that utilizes the free Google Gemini API for dynamic responses to questions. You will also learn how to incorporate exciting features such as image uploads, an emoji picker, and ensure the chatbot is fully responsive for desktop and mobile users.
By the end of this post, you’ll gain practical skills in API integration, data handling, and responsive web design, along with a standout project to boost your portfolio in today’s AI-driven digital landscape.
Video Tutorial of AI Chatbot in HTML CSS & JavaScript
The YouTube tutorial above is an excellent resource if you prefer video learning. It covers every line of code in detail, with helpful comments to make the process easy to follow. For those who prefer a written guide, keep reading as we break down each step of the chatbot project.
Steps to Create AI Chatbot HTML CSS & JavaScript
To create an interactive AI chatbot using HTML, CSS, and JavaScript, follow these simple step-by-step instructions:
- Create a folder with any name you like, e.g., ai-chatbot.
- Inside it, create the necessary files:
index.html
,style.css
, andscript.js
.
In your index.html
file, add the essential HTML structure for the chatbot. Include links to fonts and an Emoji Mark CDN to enhance the chatbot’s visual elements.
<!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>AI Chatbot | CodingNepal</title> <!-- Linking Google fonts for icons --> <link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@48,400,0,0&family=Material+Symbols+Rounded:opsz,wght,FILL,GRAD@48,400,1,0" /> <link rel="stylesheet" href="style.css" /> </head> <body> <!-- Chatbot Toggler --> <button id="chatbot-toggler"> <span class="material-symbols-rounded">mode_comment</span> <span class="material-symbols-rounded">close</span> </button> <div class="chatbot-popup"> <!-- Chatbot Header --> <div class="chat-header"> <div class="header-info"> <svg class="chatbot-logo" xmlns="http://www.w3.org/2000/svg" width="50" height="50" viewBox="0 0 1024 1024"> <path d="M738.3 287.6H285.7c-59 0-106.8 47.8-106.8 106.8v303.1c0 59 47.8 106.8 106.8 106.8h81.5v111.1c0 .7.8 1.1 1.4.7l166.9-110.6 41.8-.8h117.4l43.6-.4c59 0 106.8-47.8 106.8-106.8V394.5c0-59-47.8-106.9-106.8-106.9zM351.7 448.2c0-29.5 23.9-53.5 53.5-53.5s53.5 23.9 53.5 53.5-23.9 53.5-53.5 53.5-53.5-23.9-53.5-53.5zm157.9 267.1c-67.8 0-123.8-47.5-132.3-109h264.6c-8.6 61.5-64.5 109-132.3 109zm110-213.7c-29.5 0-53.5-23.9-53.5-53.5s23.9-53.5 53.5-53.5 53.5 23.9 53.5 53.5-23.9 53.5-53.5 53.5zM867.2 644.5V453.1h26.5c19.4 0 35.1 15.7 35.1 35.1v121.1c0 19.4-15.7 35.1-35.1 35.1h-26.5zM95.2 609.4V488.2c0-19.4 15.7-35.1 35.1-35.1h26.5v191.3h-26.5c-19.4 0-35.1-15.7-35.1-35.1zM561.5 149.6c0 23.4-15.6 43.3-36.9 49.7v44.9h-30v-44.9c-21.4-6.5-36.9-26.3-36.9-49.7 0-28.6 23.3-51.9 51.9-51.9s51.9 23.3 51.9 51.9z" /> </svg> <h2 class="logo-text">Chatbot</h2> </div> <button id="close-chatbot" class="material-symbols-rounded">keyboard_arrow_down</button> </div> <!-- Chatbot Body --> <div class="chat-body"> <div class="message bot-message"> <svg class="bot-avatar" xmlns="http://www.w3.org/2000/svg" width="50" height="50" viewBox="0 0 1024 1024"> <path d="M738.3 287.6H285.7c-59 0-106.8 47.8-106.8 106.8v303.1c0 59 47.8 106.8 106.8 106.8h81.5v111.1c0 .7.8 1.1 1.4.7l166.9-110.6 41.8-.8h117.4l43.6-.4c59 0 106.8-47.8 106.8-106.8V394.5c0-59-47.8-106.9-106.8-106.9zM351.7 448.2c0-29.5 23.9-53.5 53.5-53.5s53.5 23.9 53.5 53.5-23.9 53.5-53.5 53.5-53.5-23.9-53.5-53.5zm157.9 267.1c-67.8 0-123.8-47.5-132.3-109h264.6c-8.6 61.5-64.5 109-132.3 109zm110-213.7c-29.5 0-53.5-23.9-53.5-53.5s23.9-53.5 53.5-53.5 53.5 23.9 53.5 53.5-23.9 53.5-53.5 53.5zM867.2 644.5V453.1h26.5c19.4 0 35.1 15.7 35.1 35.1v121.1c0 19.4-15.7 35.1-35.1 35.1h-26.5zM95.2 609.4V488.2c0-19.4 15.7-35.1 35.1-35.1h26.5v191.3h-26.5c-19.4 0-35.1-15.7-35.1-35.1zM561.5 149.6c0 23.4-15.6 43.3-36.9 49.7v44.9h-30v-44.9c-21.4-6.5-36.9-26.3-36.9-49.7 0-28.6 23.3-51.9 51.9-51.9s51.9 23.3 51.9 51.9z" /> </svg> <!-- prettier-ignore --> <div class="message-text"> Hey there <br /> How can I help you today? </div> </div> </div> <!-- Chatbot Footer --> <div class="chat-footer"> <form action="#" class="chat-form"> <textarea placeholder="Message..." class="message-input" required></textarea> <div class="chat-controls"> <button type="button" id="emoji-picker" class="material-symbols-outlined">sentiment_satisfied</button> <div class="file-upload-wrapper"> <input type="file" accept="image/*" id="file-input" hidden /> <img src="#" /> <button type="button" id="file-upload" class="material-symbols-rounded">attach_file</button> <button type="button" id="file-cancel" class="material-symbols-rounded">close</button> </div> <button type="submit" id="send-message" class="material-symbols-rounded">arrow_upward</button> </div> </form> </div> </div> <!-- Linking Emoji Mart script for emoji picker --> <script src="https://cdn.jsdelivr.net/npm/emoji-mart@latest/dist/browser.js"></script> <!-- Linking custom script --> <script src="script.js"></script> </body> </html>
In your style.css
 file, style your chatbot to make it responsive and visually appealing. Experiment with colors, fonts, and backgrounds to enhance the design.
/* Importing Google Fonts - Inter */ @import url('https://fonts.googleapis.com/css2?family=Inter:opsz,[email protected],100..900&display=swap'); * { margin: 0; padding: 0; box-sizing: border-box; font-family: "Inter", sans-serif; } body { width: 100%; min-height: 100vh; background: linear-gradient(#EEEEFF, #C8C7FF); } #chatbot-toggler { position: fixed; bottom: 30px; right: 35px; border: none; height: 50px; width: 50px; display: flex; cursor: pointer; align-items: center; justify-content: center; border-radius: 50%; background: #5350C4; box-shadow: 0 0 20px rgba(0, 0, 0, 0.1); transition: all 0.2s ease; } body.show-chatbot #chatbot-toggler { transform: rotate(90deg); } #chatbot-toggler span { color: #fff; position: absolute; } #chatbot-toggler span:last-child, body.show-chatbot #chatbot-toggler span:first-child { opacity: 0; } body.show-chatbot #chatbot-toggler span:last-child { opacity: 1; } .chatbot-popup { position: fixed; right: 35px; bottom: 90px; width: 420px; overflow: hidden; background: #fff; border-radius: 15px; opacity: 0; pointer-events: none; transform: scale(0.2); transform-origin: bottom right; box-shadow: 0 0 128px 0 rgba(0, 0, 0, 0.1), 0 32px 64px -48px rgba(0, 0, 0, 0.5); transition: all 0.1s ease; } body.show-chatbot .chatbot-popup { opacity: 1; pointer-events: auto; transform: scale(1); } .chat-header { display: flex; align-items: center; padding: 15px 22px; background: #5350C4; justify-content: space-between; } .chat-header .header-info { display: flex; gap: 10px; align-items: center; } .header-info .chatbot-logo { width: 35px; height: 35px; padding: 6px; fill: #5350C4; flex-shrink: 0; background: #fff; border-radius: 50%; } .header-info .logo-text { color: #fff; font-weight: 600; font-size: 1.31rem; letter-spacing: 0.02rem; } .chat-header #close-chatbot { border: none; color: #fff; height: 40px; width: 40px; font-size: 1.9rem; margin-right: -10px; padding-top: 2px; cursor: pointer; border-radius: 50%; background: none; transition: 0.2s ease; } .chat-header #close-chatbot:hover { background: #3d39ac; } .chat-body { padding: 25px 22px; gap: 20px; display: flex; height: 460px; overflow-y: auto; margin-bottom: 82px; flex-direction: column; scrollbar-width: thin; scrollbar-color: #ccccf5 transparent; } .chat-body, .chat-form .message-input:hover { scrollbar-color: #ccccf5 transparent; } .chat-body .message { display: flex; gap: 11px; align-items: center; } .chat-body .message .bot-avatar { width: 35px; height: 35px; padding: 6px; fill: #fff; flex-shrink: 0; margin-bottom: 2px; align-self: flex-end; border-radius: 50%; background: #5350C4; } .chat-body .message .message-text { padding: 12px 16px; max-width: 75%; font-size: 0.95rem; } .chat-body .bot-message.thinking .message-text { padding: 2px 16px; } .chat-body .bot-message .message-text { background: #F2F2FF; border-radius: 13px 13px 13px 3px; } .chat-body .user-message { flex-direction: column; align-items: flex-end; } .chat-body .user-message .message-text { color: #fff; background: #5350C4; border-radius: 13px 13px 3px 13px; } .chat-body .user-message .attachment { width: 50%; margin-top: -7px; border-radius: 13px 3px 13px 13px; } .chat-body .bot-message .thinking-indicator { display: flex; gap: 4px; padding-block: 15px; } .chat-body .bot-message .thinking-indicator .dot { height: 7px; width: 7px; opacity: 0.7; border-radius: 50%; background: #6F6BC2; animation: dotPulse 1.8s ease-in-out infinite; } .chat-body .bot-message .thinking-indicator .dot:nth-child(1) { animation-delay: 0.2s; } .chat-body .bot-message .thinking-indicator .dot:nth-child(2) { animation-delay: 0.3s; } .chat-body .bot-message .thinking-indicator .dot:nth-child(3) { animation-delay: 0.4s; } @keyframes dotPulse { 0%, 44% { transform: translateY(0); } 28% { opacity: 0.4; transform: translateY(-4px); } 44% { opacity: 0.2; } } .chat-footer { position: absolute; bottom: 0; width: 100%; background: #fff; padding: 15px 22px 20px; } .chat-footer .chat-form { display: flex; align-items: center; position: relative; background: #fff; border-radius: 32px; outline: 1px solid #CCCCE5; box-shadow: 0 0 8px rgba(0, 0, 0, 0.06); transition: 0s ease, border-radius 0s; } .chat-form:focus-within { outline: 2px solid #5350C4; } .chat-form .message-input { width: 100%; height: 47px; outline: none; resize: none; border: none; max-height: 180px; scrollbar-width: thin; border-radius: inherit; font-size: 0.95rem; padding: 14px 0 12px 18px; scrollbar-color: transparent transparent; } .chat-form .chat-controls { gap: 3px; height: 47px; display: flex; padding-right: 6px; align-items: center; align-self: flex-end; } .chat-form .chat-controls button { height: 35px; width: 35px; border: none; cursor: pointer; color: #706DB0; border-radius: 50%; font-size: 1.15rem; background: none; transition: 0.2s ease; } .chat-form .chat-controls button:hover, body.show-emoji-picker .chat-controls #emoji-picker { color: #3d39ac; background: #f1f1ff; } .chat-form .chat-controls #send-message { color: #fff; display: none; background: #5350C4; } .chat-form .chat-controls #send-message:hover { background: #3d39ac; } .chat-form .message-input:valid~.chat-controls #send-message { display: block; } .chat-form .file-upload-wrapper { position: relative; height: 35px; width: 35px; } .chat-form .file-upload-wrapper :where(button, img) { position: absolute; } .chat-form .file-upload-wrapper img { height: 100%; width: 100%; object-fit: cover; border-radius: 50%; } .chat-form .file-upload-wrapper #file-cancel { color: #ff0000; background: #fff; } .chat-form .file-upload-wrapper :where(img, #file-cancel), .chat-form .file-upload-wrapper.file-uploaded #file-upload { display: none; } .chat-form .file-upload-wrapper.file-uploaded img, .chat-form .file-upload-wrapper.file-uploaded:hover #file-cancel { display: block; } em-emoji-picker { position: absolute; left: 50%; top: -337px; width: 100%; max-width: 350px; visibility: hidden; max-height: 330px; transform: translateX(-50%); } body.show-emoji-picker em-emoji-picker { visibility: visible; } /* Responsive media query for mobile screens */ @media (max-width: 520px) { #chatbot-toggler { right: 20px; bottom: 20px; } .chatbot-popup { right: 0; bottom: 0; height: 100%; border-radius: 0; width: 100%; } .chatbot-popup .chat-header { padding: 12px 15px; } .chat-body { height: calc(90% - 55px); padding: 25px 15px; } .chat-footer { padding: 10px 15px 15px; } .chat-form .file-upload-wrapper.file-uploaded #file-cancel { opacity: 0; } }
In your script.js
file, bring your chatbot to life by adding interactivity and functionality. This script will handle user messages, manage image uploads, connect to the Google Gemini API for dynamic responses, and initialize the emoji picker.
const chatBody = document.querySelector(".chat-body"); const messageInput = document.querySelector(".message-input"); const sendMessage = document.querySelector("#send-message"); const fileInput = document.querySelector("#file-input"); const fileUploadWrapper = document.querySelector(".file-upload-wrapper"); const fileCancelButton = fileUploadWrapper.querySelector("#file-cancel"); const chatbotToggler = document.querySelector("#chatbot-toggler"); const closeChatbot = document.querySelector("#close-chatbot"); // API setup const API_KEY = "PASTE-YOUR-API-KEY"; const API_URL = `https://generativelanguage.googleapis.com/v1beta/models/gemini-1.5-flash:generateContent?key=${API_KEY}`; // Initialize user message and file data const userData = { message: null, file: { data: null, mime_type: null, }, }; // Store chat history const chatHistory = []; const initialInputHeight = messageInput.scrollHeight; // Create message element with dynamic classes and return it const createMessageElement = (content, ...classes) => { const div = document.createElement("div"); div.classList.add("message", ...classes); div.innerHTML = content; return div; }; // Generate bot response using API const generateBotResponse = async (incomingMessageDiv) => { const messageElement = incomingMessageDiv.querySelector(".message-text"); // Add user message to chat history chatHistory.push({ role: "user", parts: [{ text: userData.message }, ...(userData.file.data ? [{ inline_data: userData.file }] : [])], }); // API request options const requestOptions = { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ contents: chatHistory, }), }; try { // Fetch bot response from API const response = await fetch(API_URL, requestOptions); const data = await response.json(); if (!response.ok) throw new Error(data.error.message); // Extract and display bot's response text const apiResponseText = data.candidates[0].content.parts[0].text.replace(/\*\*(.*?)\*\*/g, "$1").trim(); messageElement.innerText = apiResponseText; // Add bot response to chat history chatHistory.push({ role: "model", parts: [{ text: apiResponseText }], }); } catch (error) { // Handle error in API response console.log(error); messageElement.innerText = error.message; messageElement.style.color = "#ff0000"; } finally { // Reset user's file data, removing thinking indicator and scroll chat to bottom userData.file = {}; incomingMessageDiv.classList.remove("thinking"); chatBody.scrollTo({ top: chatBody.scrollHeight, behavior: "smooth" }); } }; // Handle outgoing user messages const handleOutgoingMessage = (e) => { e.preventDefault(); userData.message = messageInput.value.trim(); messageInput.value = ""; messageInput.dispatchEvent(new Event("input")); fileUploadWrapper.classList.remove("file-uploaded"); // Create and display user message const messageContent = `<div class="message-text"></div> ${userData.file.data ? `<img src="data:${userData.file.mime_type};base64,${userData.file.data}" class="attachment" />` : ""}`; const outgoingMessageDiv = createMessageElement(messageContent, "user-message"); outgoingMessageDiv.querySelector(".message-text").innerText = userData.message; chatBody.appendChild(outgoingMessageDiv); chatBody.scrollTo({ top: chatBody.scrollHeight, behavior: "smooth" }); // Simulate bot response with thinking indicator after a delay setTimeout(() => { const messageContent = `<svg class="bot-avatar" xmlns="http://www.w3.org/2000/svg" width="50" height="50" viewBox="0 0 1024 1024"> <path d="M738.3 287.6H285.7c-59 0-106.8 47.8-106.8 106.8v303.1c0 59 47.8 106.8 106.8 106.8h81.5v111.1c0 .7.8 1.1 1.4.7l166.9-110.6 41.8-.8h117.4l43.6-.4c59 0 106.8-47.8 106.8-106.8V394.5c0-59-47.8-106.9-106.8-106.9zM351.7 448.2c0-29.5 23.9-53.5 53.5-53.5s53.5 23.9 53.5 53.5-23.9 53.5-53.5 53.5-53.5-23.9-53.5-53.5zm157.9 267.1c-67.8 0-123.8-47.5-132.3-109h264.6c-8.6 61.5-64.5 109-132.3 109zm110-213.7c-29.5 0-53.5-23.9-53.5-53.5s23.9-53.5 53.5-53.5 53.5 23.9 53.5 53.5-23.9 53.5-53.5 53.5zM867.2 644.5V453.1h26.5c19.4 0 35.1 15.7 35.1 35.1v121.1c0 19.4-15.7 35.1-35.1 35.1h-26.5zM95.2 609.4V488.2c0-19.4 15.7-35.1 35.1-35.1h26.5v191.3h-26.5c-19.4 0-35.1-15.7-35.1-35.1zM561.5 149.6c0 23.4-15.6 43.3-36.9 49.7v44.9h-30v-44.9c-21.4-6.5-36.9-26.3-36.9-49.7 0-28.6 23.3-51.9 51.9-51.9s51.9 23.3 51.9 51.9z"/></svg> <div class="message-text"> <div class="thinking-indicator"> <div class="dot"></div> <div class="dot"></div> <div class="dot"></div> </div> </div>`; const incomingMessageDiv = createMessageElement(messageContent, "bot-message", "thinking"); chatBody.appendChild(incomingMessageDiv); chatBody.scrollTo({ top: chatBody.scrollHeight, behavior: "smooth" }); generateBotResponse(incomingMessageDiv); }, 600); }; // Adjust input field height dynamically messageInput.addEventListener("input", () => { messageInput.style.height = `${initialInputHeight}px`; messageInput.style.height = `${messageInput.scrollHeight}px`; document.querySelector(".chat-form").style.borderRadius = messageInput.scrollHeight > initialInputHeight ? "15px" : "32px"; }); // Handle Enter key press for sending messages messageInput.addEventListener("keydown", (e) => { const userMessage = e.target.value.trim(); if (e.key === "Enter" && !e.shiftKey && userMessage && window.innerWidth > 768) { handleOutgoingMessage(e); } }); // Handle file input change and preview the selected file fileInput.addEventListener("change", () => { const file = fileInput.files[0]; if (!file) return; const reader = new FileReader(); reader.onload = (e) => { fileInput.value = ""; fileUploadWrapper.querySelector("img").src = e.target.result; fileUploadWrapper.classList.add("file-uploaded"); const base64String = e.target.result.split(",")[1]; // Store file data in userData userData.file = { data: base64String, mime_type: file.type, }; }; reader.readAsDataURL(file); }); // Cancel file upload fileCancelButton.addEventListener("click", () => { userData.file = {}; fileUploadWrapper.classList.remove("file-uploaded"); }); // Initialize emoji picker and handle emoji selection const picker = new EmojiMart.Picker({ theme: "light", skinTonePosition: "none", previewPosition: "none", onEmojiSelect: (emoji) => { const { selectionStart: start, selectionEnd: end } = messageInput; messageInput.setRangeText(emoji.native, start, end, "end"); messageInput.focus(); }, onClickOutside: (e) => { if (e.target.id === "emoji-picker") { document.body.classList.toggle("show-emoji-picker"); } else { document.body.classList.remove("show-emoji-picker"); } }, }); document.querySelector(".chat-form").appendChild(picker); sendMessage.addEventListener("click", (e) => handleOutgoingMessage(e)); document.querySelector("#file-upload").addEventListener("click", () => fileInput.click()); closeChatbot.addEventListener("click", () => document.body.classList.remove("show-chatbot")); chatbotToggler.addEventListener("click", () => document.body.classList.toggle("show-chatbot"));
Important: Your chatbot won’t be able to generate responses until you configure it with a Gemini API key. To do this, simply add your API key to the API_KEY variable in the script.js
file. You can get a free API key from Google AI Studio. The key will look something like this: AIzaSyAtpnKGX14bTgmx0l_gQeatYvdWvY_wOTQ.
Once your API key is added to the code, your chatbot will be ready for action! Just open the index.html
file in your browser, and you can start chatting instantly.
For further customization, you can personalize the chatbot to include your company information so it responds accordingly. To do this, simply update line 24 with the following code:
const chatHistory = [{ role: "model", parts: [{ text: "Your company information here" }], }];
Conclusion and final words
Congratulations! You’ve built your AI-powered chatbot using HTML, CSS, and JavaScript. By integrating the Google Gemini API, you’ve created an interactive tool that provides dynamic responses to user inquiries. With features like image uploads and an emoji picker, your chatbot is both functional and engaging.
As you customize your chatbot, you’ll sharpen your skills in web development and API integration — crucial tools in today’s tech landscape. Whether you showcase this project in your portfolio or use it as a stepping stone for future endeavors, you’ve taken a significant step in leveraging AI in web applications.
Feel free to experiment further! Enhance your chatbot’s capabilities, explore new features, and make it uniquely yours. Happy coding!
No, Google Has a Limit
Please check here
https://ai.google.dev/pricing#1_5flash
Sir, it’s excellent and working fine but it’s unlimited or is their any limit for chat please response sir
Bro i dont think that you should give source code for free,pls.I think selling your code will get you more views and likes
hello please re upload chatapp to live on online server because 000webhost is closed or suggest some other site name
I love the style of this! I was online to see it go up!