In today’s world, AI chatbots like Google Gemini and ChatGPT are changing how we interact with technology. They provide more human-like conversations and smarter responses. Have you ever thought about creating your own chatbot similar to Google Gemini? The good news is, that with HTML, CSS, and JavaScript, you can build a chatbot interface that mimics Gemini’s conversational style.
Google Gemini is a smart AI chatbot created by Google, much like ChatGPT. It uses artificial intelligence to generate natural, human-like responses, making interactions feel smoother and easier. While building an AI model like Google Gemini involves complex machine learning, you can create a simpler version of the chatbot interface using basic web development tools. This tutorial will show you how to build a fully functional chatbot interface with HTML, CSS, and JavaScript.
Why Build a Chatbot Interface?
Building a chatbot interface like Gemini provides valuable experience in web development, user interface (UI) design, and API integration. By following this tutorial, you’ll learn how to structure a dynamic chatbot layout, style it with CSS, and make it interactive with JavaScript.
Additionally, the chatbot we’ll create will support essential features like:
- Sending and receiving messages
- Toggling between light and dark themes
- Saving chat history and user preferences in local storage
This project will help you level up your front-end development skills and give you a foundation for building more advanced web applications in the future.
Steps to Build a Google Gemini Chatbot Clone
Step 1: Setting Up Your Project Folder
To start, create a new folder for your project. You can name it something like gemini-chatbot. Inside this folder, you will need the following three files:
- index.html – This will contain the HTML structure of your chatbot interface.
- style.css – This file will handle the styling of the chatbot, giving it a sleek, responsive design.
- script.js – Here, you’ll write the JavaScript code that makes the chatbot interactive.
You can also download and include images, such as logos or background graphics, to make the chatbot interface more appealing.
Step 2: Structuring the HTML (index.html)
The HTML file is responsible for the layout and structure of your chatbot. Start by creating the basic framework for your chatbot interface, which will include the following sections:
- A header – This will display the chatbot name and theme toggle button (light/dark).
- A chat area – This section will hold the conversation bubbles and display messages.
- An input area – At the bottom of the page, you’ll have a text input box where users can type their messages and a button to send them.
Make sure to use semantic HTML tags for better accessibility and readability. Here you can understand the structure through this example in a simplified way:
<!DOCTYPE html><!-- Coding By Abhikesh - www.abhikesh.com --><html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Gemini Chatbot | Abhikesh Kumar</title><!-- Linking Google Fonts For Icons --><link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Material+Symbols+Rounded:opsz,wght,FILL,GRAD@24,400,0,0" /><link rel="stylesheet" href="style.css"></head><body><header class="header"><!-- Header Greetings --><h1 class="title">Hello, there</h1><p class="subtitle">How can I help you today?</p><!-- Suggestion list --><ul class="suggestion-list"><li class="suggestion"><h4 class="text">Help me plan a game night with my 5 best friends for under $100.</h4><span class="icon material-symbols-rounded">draw</span></li><li class="suggestion"><h4 class="text">What are the best tips to improve my public speaking skills?</h4><span class="icon material-symbols-rounded">lightbulb</span></li><li class="suggestion"><h4 class="text">Can you help me find the latest news on web development?</h4><span class="icon material-symbols-rounded">explore</span></li><li class="suggestion"><h4 class="text">Write JavaScript code to sum all elements in an array.</h4><span class="icon material-symbols-rounded">code</span></li></ul></header><!-- Chat List / Container --><div class="chat-list"></div><!-- Typing Area --><div class="typing-area"><form action="#" class="typing-form"><div class="input-wrapper"><input type="text" placeholder="Enter a prompt here" class="typing-input" required /><button id="send-message-button" class="icon material-symbols-rounded">send</button></div><div class="action-buttons"><span id="theme-toggle-button" class="icon material-symbols-rounded">light_mode</span><span id="delete-chat-button" class="icon material-symbols-rounded">delete</span></div></form><p class="disclaimer-text">Gemini may display inaccurate info, including about people, so double-check its responses.</p></div><script src="script.js"></script></body></html>
Step 3: Styling with CSS (style.css)
Now that your HTML structure is ready, it’s time to make it visually appealing with CSS. The style file will control the layout, colors, font styles, and responsiveness of your chatbot.
Focus on creating a minimalist and modern look, similar to Google’s Gemini chatbot. You can add a light and dark theme toggle by using CSS variables for colors and switching them based on the user’s selection.
For example
/* 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 mode colors */--text-color: #E3E3E3;--subheading-color: #828282;--placeholder-color: #A6A6A6;--primary-color: #242424;--secondary-color: #383838;--secondary-hover-color: #444;}.light_mode {/* Light mode colors */--text-color: #222;--subheading-color: #A0A0A0;--placeholder-color: #6C6C6C;--primary-color: #FFF;--secondary-color: #E9EEF6;--secondary-hover-color: #DBE1EA;}body {background: var(--primary-color);}.header, .chat-list .message, .typing-form {margin: 0 auto;max-width: 980px;}.header {margin-top: 6vh;padding: 1rem;overflow-x: hidden;}body.hide-header .header {margin: 0;display: none;}.header :where(.title, .subtitle) {color: var(--text-color);font-weight: 500;line-height: 4rem;}.header .title {width: fit-content;font-size: 3rem;background-clip: text;background: linear-gradient(to right, #4285f4, #d96570);-webkit-background-clip: text;-webkit-text-fill-color: transparent;}.header .subtitle {font-size: 2.6rem;color: var(--subheading-color);}.suggestion-list {width: 100%;list-style: none;display: flex;gap: 1.25rem;margin-top: 9.5vh;overflow: hidden;overflow-x: auto;scroll-snap-type: x mandatory;scrollbar-width: none;}.suggestion-list .suggestion {cursor: pointer;padding: 1.25rem;width: 222px;flex-shrink: 0;display: flex;flex-direction: column;align-items: flex-end;border-radius: 0.75rem;justify-content: space-between;background: var(--secondary-color);transition: 0.2s ease;}.suggestion-list .suggestion:hover {background: var(--secondary-hover-color);}.suggestion-list .suggestion :where(.text, .icon) {font-weight: 400;color: var(--text-color);}.suggestion-list .suggestion .icon {width: 42px;height: 42px;display: flex;font-size: 1.3rem;margin-top: 2.5rem;align-self: flex-end;align-items: center;border-radius: 50%;justify-content: center;color: var(--text-color);background: var(--primary-color);}.chat-list {padding: 2rem 1rem 12rem;max-height: 100vh;overflow-y: auto;scrollbar-color: #999 transparent;}.chat-list .message.incoming {margin-top: 1.5rem;}.chat-list .message .message-content {display: flex;gap: 1.5rem;width: 100%;align-items: center;}.chat-list .message .text {color: var(--text-color);white-space: pre-wrap;}.chat-list .message.error .text {color: #e55865;}.chat-list .message.loading .text {display: none;}.chat-list .message .avatar {width: 40px;height: 40px;object-fit: cover;border-radius: 50%;align-self: flex-start;}.chat-list .message.loading .avatar {animation: rotate 3s linear infinite;}@keyframes rotate {100% {transform: rotate(360deg);}}.chat-list .message .icon {color: var(--text-color);cursor: pointer;height: 35px;width: 35px;border-radius: 50%;display: flex;align-items: center;justify-content: center;background: none;font-size: 1.25rem;margin-left: 3.5rem;visibility: hidden;}.chat-list .message .icon.hide {visibility: hidden;}.chat-list .message:not(.loading, .error):hover .icon:not(.hide){visibility: visible;}.chat-list .message .icon:hover {background: var(--secondary-hover-color);}.chat-list .message .loading-indicator {display: none;gap: 0.8rem;width: 100%;flex-direction: column;}.chat-list .message.loading .loading-indicator {display: flex;}.chat-list .message .loading-indicator .loading-bar {height: 11px;width: 100%;border-radius: 0.135rem;background-position: -800px 0;background: linear-gradient(to right, #4285f4, var(--primary-color), #4285f4);animation: loading 3s linear infinite;}.chat-list .message .loading-indicator .loading-bar:last-child {width: 70%;}@keyframes loading {0% {background-position: -800px 0;}100% {background-position: 800px 0;}}.typing-area {position: fixed;width: 100%;left: 0;bottom: 0;padding: 1rem;background: var(--primary-color);}.typing-area :where(.typing-form, .action-buttons) {display: flex;gap: 0.75rem;}.typing-form .input-wrapper {width: 100%;height: 56px;display: flex;position: relative;}.typing-form .typing-input {height: 100%;width: 100%;border: none;outline: none;resize: none;font-size: 1rem;color: var(--text-color);padding: 1.1rem 4rem 1.1rem 1.5rem;border-radius: 100px;background: var(--secondary-color);}.typing-form .typing-input:focus {background: var(--secondary-hover-color);}.typing-form .typing-input::placeholder {color: var(--placeholder-color);}.typing-area .icon {width: 56px;height: 56px;flex-shrink: 0;cursor: pointer;border-radius: 50%;display: flex;font-size: 1.4rem;color: var(--text-color);align-items: center;justify-content: center;background: var(--secondary-color);transition: 0.2s ease;}.typing-area .icon:hover {background: var(--secondary-hover-color);}.typing-form #send-message-button {position: absolute;right: 0;outline: none;border: none;transform: scale(0);background: transparent;transition: transform 0.2s ease;}.typing-form .typing-input:valid ~ #send-message-button {transform: scale(1);}.typing-area .disclaimer-text {text-align: center;font-size: 0.85rem;margin-top: 1rem;color: var(--placeholder-color);}/* Responsive media query code for small screen */@media (max-width: 768px) {.header :is(.title, .subtitle) {font-size: 2rem;line-height: 2.6rem;}.header .subtitle {font-size: 1.7rem;}.typing-area :where(.typing-form, .action-buttons) {gap: 0.4rem;}.typing-form .input-wrapper {height: 50px;}.typing-form .typing-input {padding: 1.1rem 3.5rem 1.1rem 1.2rem;}.typing-area .icon {height: 50px;width: 50px;}.typing-area .disclaimer-text {font-size: 0.75rem;margin-top: 0.5rem;}}
Step 4: Adding Interactivity with JavaScript (script.js)
The final step is adding interactivity using JavaScript. This involves sending messages, toggling between themes, and storing chat history in the browser’s local storage.
Your chatbot will need functions to:
- Send messages – Capture the text from the input field and display it in the chat area.
- Toggle themes – Switch between light and dark themes by adding/removing the dark-theme class.
- Store chat history – Save the chat history in local Storage so it persists even after a page refresh.
Here’s a basic implementation of these features in JavaScript:
const typingForm = document.querySelector(".typing-form");const chatContainer = document.querySelector(".chat-list");const suggestions = document.querySelectorAll(".suggestion");const toggleThemeButton = document.querySelector("#theme-toggle-button");const deleteChatButton = document.querySelector("#delete-chat-button");// State variableslet userMessage = null;let isResponseGenerating = false;// API configurationconst API_KEY = "PASTE-YOUR-API-KEY"; // Your API key hereconst API_URL = `https://generativelanguage.googleapis.com/v1/models/gemini-pro:generateContent?key=${API_KEY}`;// Load theme and chat data from local storage on page loadconst loadDataFromLocalstorage = () => {const savedChats = localStorage.getItem("saved-chats");const isLightMode = (localStorage.getItem("themeColor") === "light_mode");// Apply the stored themedocument.body.classList.toggle("light_mode", isLightMode);toggleThemeButton.innerText = isLightMode ? "dark_mode" : "light_mode";// Restore saved chats or clear the chat containerchatContainer.innerHTML = savedChats || '';document.body.classList.toggle("hide-header", savedChats);chatContainer.scrollTo(0, chatContainer.scrollHeight); // Scroll to the bottom}// Create a new message element and return itconst createMessageElement = (content, ...classes) => {const div = document.createElement("div");div.classList.add("message", ...classes);div.innerHTML = content;return div;}// Show typing effect by displaying words one by oneconst showTypingEffect = (text, textElement, incomingMessageDiv) => {const words = text.split(' ');let currentWordIndex = 0;const typingInterval = setInterval(() => {// Append each word to the text element with a spacetextElement.innerText += (currentWordIndex === 0 ? '' : ' ') + words[currentWordIndex++];incomingMessageDiv.querySelector(".icon").classList.add("hide");// If all words are displayedif (currentWordIndex === words.length) {clearInterval(typingInterval);isResponseGenerating = false;incomingMessageDiv.querySelector(".icon").classList.remove("hide");localStorage.setItem("saved-chats", chatContainer.innerHTML); // Save chats to local storage}chatContainer.scrollTo(0, chatContainer.scrollHeight); // Scroll to the bottom}, 75);}// Fetch response from the API based on user messageconst generateAPIResponse = async (incomingMessageDiv) => {const textElement = incomingMessageDiv.querySelector(".text"); // Getting text elementtry {// Send a POST request to the API with the user's messageconst response = await fetch(API_URL, {method: "POST",headers: { "Content-Type": "application/json" },body: JSON.stringify({contents: [{role: "user",parts: [{ text: userMessage }]}]}),});const data = await response.json();if (!response.ok) throw new Error(data.error.message);// Get the API response text and remove asterisks from itconst apiResponse = data?.candidates[0].content.parts[0].text.replace(/\*\*(.*?)\*\*/g, '$1');showTypingEffect(apiResponse, textElement, incomingMessageDiv); // Show typing effect} catch (error) { // Handle errorisResponseGenerating = false;textElement.innerText = error.message;textElement.parentElement.closest(".message").classList.add("error");} finally {incomingMessageDiv.classList.remove("loading");}}// Show a loading animation while waiting for the API responseconst showLoadingAnimation = () => {const html = `<div class="message-content"><img class="avatar" src="images/gemini.svg" alt="Gemini avatar"><p class="text"></p><div class="loading-indicator"><div class="loading-bar"></div><div class="loading-bar"></div><div class="loading-bar"></div></div></div><span onClick="copyMessage(this)" class="icon material-symbols-rounded">content_copy</span>`;const incomingMessageDiv = createMessageElement(html, "incoming", "loading");chatContainer.appendChild(incomingMessageDiv);chatContainer.scrollTo(0, chatContainer.scrollHeight); // Scroll to the bottomgenerateAPIResponse(incomingMessageDiv);}// Copy message text to the clipboardconst copyMessage = (copyButton) => {const messageText = copyButton.parentElement.querySelector(".text").innerText;navigator.clipboard.writeText(messageText);copyButton.innerText = "done"; // Show confirmation iconsetTimeout(() => copyButton.innerText = "content_copy", 1000); // Revert icon after 1 second}// Handle sending outgoing chat messagesconst handleOutgoingChat = () => {userMessage = typingForm.querySelector(".typing-input").value.trim() || userMessage;if(!userMessage || isResponseGenerating) return; // Exit if there is no message or response is generatingisResponseGenerating = true;const html = `<div class="message-content"><img class="avatar" src="images/user.jpg" alt="User avatar"><p class="text"></p></div>`;const outgoingMessageDiv = createMessageElement(html, "outgoing");outgoingMessageDiv.querySelector(".text").innerText = userMessage;chatContainer.appendChild(outgoingMessageDiv);typingForm.reset(); // Clear input fielddocument.body.classList.add("hide-header");chatContainer.scrollTo(0, chatContainer.scrollHeight); // Scroll to the bottomsetTimeout(showLoadingAnimation, 500); // Show loading animation after a delay}// Toggle between light and dark themestoggleThemeButton.addEventListener("click", () => {const isLightMode = document.body.classList.toggle("light_mode");localStorage.setItem("themeColor", isLightMode ? "light_mode" : "dark_mode");toggleThemeButton.innerText = isLightMode ? "dark_mode" : "light_mode";});// Delete all chats from local storage when button is clickeddeleteChatButton.addEventListener("click", () => {if (confirm("Are you sure you want to delete all the chats?")) {localStorage.removeItem("saved-chats");loadDataFromLocalstorage();}});// Set userMessage and handle outgoing chat when a suggestion is clickedsuggestions.forEach(suggestion => {suggestion.addEventListener("click", () => {userMessage = suggestion.querySelector(".text").innerText;handleOutgoingChat();});});// Prevent default form submission and handle outgoing chattypingForm.addEventListener("submit", (e) => {e.preventDefault();handleOutgoingChat();});loadDataFromLocalstorage();
Conclusion and Final Words
You’ve successfully built a Google Gemini chatbot interface using HTML, CSS, and JavaScript. This project is an excellent way to sharpen your front-end development skills, as it incorporates responsive design, theme switching, and local storage management. You’ve also learned the process of creating an interactive web application that mimics real-world chatbots.
This chatbot interface can be further enhanced by integrating an AI-powered API to generate intelligent responses, just like the actual Google Gemini chatbot.
If you encounter any challenges while building your chatbot or need further assistance, feel free to download the source code files for this project by clicking the “Download” button below.