Create ChatGPT UI Clone with HTML, CSS & JavaScript

Faraz

By Faraz -

Learn how to build a ChatGPT UI clone using HTML, CSS, and JavaScript with simple steps and clean design. Ideal for beginners and developers.


create-chatgpt-ui-clone-with-html-css-and-javascript.webp

Table of Contents

  1. Project Introduction
  2. HTML Code
  3. CSS Code
  4. JavaScript Code
  5. Conclusion
  6. Preview

Are you curious about how ChatGPT’s sleek and modern interface is built? In this guide, you will learn how to create a ChatGPT UI clone using HTML, CSS, and JavaScript. This is a great project for beginners who want to improve their front-end skills and practice responsive layouts.

We’ll use Tailwind CSS for styling, Heroicons for icons, and JavaScript to make the chat box functional. The final result will look like a mini version of the real ChatGPT interface.

Prerequisites

Before starting, make sure you have the following:

  • Basic knowledge of HTML, CSS, and JavaScript
  • A code editor like VS Code
  • A browser like Chrome or Firefox
  • Internet connection to load CDN links (Tailwind & Heroicons)

Source Code

Step 1 (HTML Code):

To get started, we first need to create a basic HTML file. In this file, we will include the main structure for our chatgpt clone. Lets breakdown the HTML code step by step:

Document Type Declaration

<!DOCTYPE html>
  • Declares the HTML document type.
  • Ensures the browser renders the page in standards mode.

HTML Root Element

<html lang="en">
  • Defines the root of the HTML document.
  • lang="en" specifies the language as English.

Head Section

<head>
  ...
</head>
  • Contains metadata and resource links for the page.

Key Elements:

Charset:

<meta charset="UTF-8">
  • Sets the character encoding to UTF-8.

Viewport for Responsiveness:

<meta name="viewport" content="width=device-width, initial-scale=1.0">
  • Ensures proper scaling on different devices.

Page Title:

<title>ChatGPT UI Clone</title>
  • Sets the browser tab title.

Tailwind CSS CDN:

<script src="https://cdn.tailwindcss.com"></script>
  • Loads Tailwind CSS for utility-first styling.

Google Fonts (Inter):

<link href="https://fonts.googleapis.com/css2?family=Inter:..." rel="stylesheet">
  • Adds a custom web font.

Heroicons Script:

<script src="https://unpkg.com/[email protected]/..."></script>
  • Loads Heroicons for UI icons.

External CSS File:

<link rel="stylesheet" href="styles.css">
  • Links to custom styles.

Body Element

<body class="text-gray-200">
  ...
</body>
  • Defines the content of the page.
  • Sets default text color using Tailwind.

Layout Container

<div class="flex h-screen">
  • Creates a full-height flex container.

Sidebar

<aside id="sidebar" class="...">
  • Fixed sidebar layout.
  • Includes header, pinned chats, recent chats, and user profile.

Key Features:

  • Brand Header with Logo and Name
  • Pinned Conversation Block
  • Recent Chat History List
  • User Profile Button at Bottom

Main Content Area

<main class="flex-1 ...">
  • Occupies the remaining space after the sidebar.

Subsections:

Mobile Header:

<header class="md:hidden ...">
  • Shown only on mobile screens.

Chat Area:

<div id="chat-container" class="...">
  • Scrollable chat container with welcome message.

Input Section:

<div class="w-full bg-gradient-to-t ...">
  • Contains a message box and tool popup.

Tools Popup

<div id="tools-popup" class="...">
  • Displays buttons for additional features like:
    • Create an image
    • Deep research
    • Code editor
    • Upload file

Chat Input Form

<form id="chat-form" class="...">
  • Includes:
    • Tool button to toggle tool popup.
    • Textarea to write a message.
    • Send button with arrow icon.

Overlay (Mobile Support)

<div id="overlay" class="..."></div>
  • Dark overlay when the sidebar is open on mobile.

Script

<script src="script.js"></script>
  • Links to external JavaScript functionality.

Step 2 (CSS Code):

Once the basic HTML structure of the clone is in place, the next step is to add styling to the chatgpt clone using CSS. Lets breakdown the CSS code step by step:

1. Body Styling

body {
  font-family: 'Inter', sans-serif;
  background-color: #171717; 
}
  • Sets the font to 'Inter', with a fallback to any sans-serif font.
  • Applies a dark background color (#171717) for the whole page.

2. Custom Scrollbar Styling

.custom-scrollbar::-webkit-scrollbar {
  width: 8px;
}
  • Target: Scrollbar inside any element with .custom-scrollbar class.
  • Effect: Sets scrollbar width to 8px.
.custom-scrollbar::-webkit-scrollbar-track {
  background: #171717;
}
  • Track background: Dark background, same as page, giving it a clean and seamless look.
.custom-scrollbar::-webkit-scrollbar-thumb {
  background: #4A4A4A;
  border-radius: 4px;
  border: 2px solid #171717;
}
  • Thumb color: Sets scrollbar thumb (handle) to medium gray.
  • Rounded corners: border-radius: 4px.
  • Border: Matches background to blend with track.
.custom-scrollbar::-webkit-scrollbar-thumb:hover {
  background: #6c6c6c;
}
  • Hover effect: Lightens the scrollbar thumb color on hover for interactivity.

3. Textarea Styling

textarea {
  resize: none;
}
  • Disables resizing of <textarea> element.
  • Prevents the user from dragging to increase or decrease its size.

4. Tools Popup Animation

.tools-popup {
  transition: opacity 0.2s ease-in-out, transform 0.2s ease-in-out;
}
  • Smooth transition: Applies animation when .tools-popup appears or disappears.
  • Effects included:
    • opacity: fade effect.
    • transform: scale or position animation.
  • Duration: 0.2s (200 milliseconds).
  • Easing: ease-in-out for smooth start and end.
body {
  font-family: 'Inter', sans-serif;
  background-color: #171717; 
}
/* Custom scrollbar for a more polished look */
.custom-scrollbar::-webkit-scrollbar {
  width: 8px;
}
.custom-scrollbar::-webkit-scrollbar-track {
  background: #171717;
}
.custom-scrollbar::-webkit-scrollbar-thumb {
  background: #4A4A4A;
  border-radius: 4px;
  border: 2px solid #171717;
}
.custom-scrollbar::-webkit-scrollbar-thumb:hover {
  background: #6c6c6c;
}
/* Style for the textarea to prevent resizing handle */
textarea {
  resize: none;
}
/* Animation for the tools popup */
.tools-popup {
  transition: opacity 0.2s ease-in-out, transform 0.2s ease-in-out;
} 

Step 3 (JavaScript Code):

Finally, we need to create a function in JavaScript. Lets breakdown the JavaScript code step by step:

DOM Elements Selection

const chatForm = document.getElementById('chat-form');
const chatInput = document.getElementById('chat-input');
const chatContainer = document.getElementById('chat-container').querySelector('.max-w-4xl');
const welcomeMessage = document.getElementById('welcome-message');
const menuBtn = document.getElementById('menu-btn');
const sidebar = document.getElementById('sidebar');
const overlay = document.getElementById('overlay');
const toolsBtn = document.getElementById('tools-btn');
const toolsPopup = document.getElementById('tools-popup');
  • Selects and stores references to important elements used throughout the UI:
    • Chat form, input, and message container.
    • Sidebar and overlay for mobile view.
    • Tools pop-up and button.
    • Welcome message section.

Event Listeners

chatForm.addEventListener('submit', handleSendMessage);
chatInput.addEventListener('input', autoResizeTextarea);
chatInput.addEventListener('keydown', handleKeydown);
menuBtn.addEventListener('click', toggleSidebar);
overlay.addEventListener('click', toggleSidebar);
toolsBtn.addEventListener('click', toggleToolsPopup);
  • Form Submit: Sends the message when the form is submitted.
  • Input Resize: Automatically resizes the textarea.
  • Enter Key: Submits message on Enter (without Shift).
  • Menu Button & Overlay: Toggles sidebar visibility.
  • Tools Button: Shows/hides tools popup.

Click Outside to Close Tools Popup

document.addEventListener('click', (e) => {
  if (!toolsPopup.contains(e.target) && !toolsBtn.contains(e.target)) {
    hideToolsPopup();
  }
});
  • Closes the tools popup when clicking outside of it.

Functions

1. toggleToolsPopup()

function toggleToolsPopup() {
  const isHidden = toolsPopup.classList.contains('opacity-0');
  if (isHidden) {
    toolsPopup.classList.remove('opacity-0', 'scale-95', 'pointer-events-none');
  } else {
    hideToolsPopup();
  }
}
  • Toggles visibility of the tools popup using Tailwind utility classes.

2. hideToolsPopup()

function hideToolsPopup() {
  toolsPopup.classList.add('opacity-0', 'scale-95', 'pointer-events-none');
}
  • Hides the tools popup by adding relevant classes.

3. toggleSidebar()

function toggleSidebar() {
  sidebar.classList.toggle('-translate-x-full');
  overlay.classList.toggle('hidden');
}
  • Shows/hides the sidebar and overlay, mainly for mobile responsiveness.

4. handleSendMessage()

function handleSendMessage(e) {
  e.preventDefault();
  const message = chatInput.value.trim();
  if (!message) return;

  if (welcomeMessage) {
    welcomeMessage.style.display = 'none';
  }

  appendMessage(message, 'user');

  chatInput.value = '';
  autoResizeTextarea();
  chatInput.style.height = 'auto';

  setTimeout(() => {
    const botResponse = '...';
    appendMessage(botResponse, 'bot');
  }, 1200);
}
  • Handles user message submission:
    • Prevents default form behavior.
    • Hides welcome message on first input.
    • Appends the user's message to chat.
    • Clears and resets the textarea.
    • Simulates a bot reply after 1.2 seconds.

5. appendMessage(text, sender)

function appendMessage(text, sender) { ... }
  • Creates message blocks dynamically and appends them to the chat area.
  • Includes:
    • Avatar (initial for user, icon for bot).
    • Message text.
    • Different background styling for bot messages.

6. autoResizeTextarea()

function autoResizeTextarea() {
  chatInput.style.height = 'auto';
  const maxHeight = 200;
  const newHeight = Math.min(chatInput.scrollHeight, maxHeight);
  chatInput.style.height = newHeight + 'px';
}
  • Automatically resizes the textarea height based on content.
  • Caps height to 200px to avoid growing too large.

7. handleKeydown(e)

function handleKeydown(e) {
  if (e.key === 'Enter' && !e.shiftKey) {
    e.preventDefault();
    chatForm.requestSubmit();
  }
}
  • Listens for Enter key:
    • Sends the message when Enter is pressed.
    • Allows a newline only when Shift + Enter is used.
// --- DOM Elements ---
const chatForm = document.getElementById('chat-form');
const chatInput = document.getElementById('chat-input');
const chatContainer = document
  .getElementById('chat-container')
  .querySelector('.max-w-4xl');
const welcomeMessage = document.getElementById('welcome-message');
const menuBtn = document.getElementById('menu-btn');
const sidebar = document.getElementById('sidebar');
const overlay = document.getElementById('overlay');
const toolsBtn = document.getElementById('tools-btn');
const toolsPopup = document.getElementById('tools-popup');

// --- Event Listeners ---
chatForm.addEventListener('submit', handleSendMessage);
chatInput.addEventListener('input', autoResizeTextarea);
chatInput.addEventListener('keydown', handleKeydown);
menuBtn.addEventListener('click', toggleSidebar);
overlay.addEventListener('click', toggleSidebar);
toolsBtn.addEventListener('click', toggleToolsPopup);

// Close popup if clicking outside
document.addEventListener('click', (e) => {
  if (!toolsPopup.contains(e.target) && !toolsBtn.contains(e.target)) {
    hideToolsPopup();
  }
});

// --- Functions ---

function toggleToolsPopup() {
  const isHidden = toolsPopup.classList.contains('opacity-0');
  if (isHidden) {
    toolsPopup.classList.remove('opacity-0', 'scale-95', 'pointer-events-none');
  } else {
    hideToolsPopup();
  }
}

function hideToolsPopup() {
  toolsPopup.classList.add('opacity-0', 'scale-95', 'pointer-events-none');
}

function toggleSidebar() {
  sidebar.classList.toggle('-translate-x-full');
  overlay.classList.toggle('hidden');
}

function handleSendMessage(e) {
  e.preventDefault();
  const message = chatInput.value.trim();
  if (!message) return;

  if (welcomeMessage) {
    welcomeMessage.style.display = 'none';
  }

  appendMessage(message, 'user');

  chatInput.value = '';
  autoResizeTextarea();
  chatInput.style.height = 'auto';

  setTimeout(() => {
    const botResponse =
      'This is a simulated 2025 response. The UI has been updated to reflect the latest design trends, including a consolidated tools menu and a more polished aesthetic. Full functionality requires a backend connection.';
    appendMessage(botResponse, 'bot');
  }, 1200);
}

function appendMessage(text, sender) {
  const isUser = sender === 'user';
  const messageWrapper = document.createElement('div');
  messageWrapper.className = `w-full ${isUser ? '' : 'bg-black/20'}`;

  const messageDiv = document.createElement('div');
  messageDiv.className = 'max-w-4xl mx-auto p-4 md:p-6 flex items-start gap-5';

  const avatarDiv = document.createElement('div');
  avatarDiv.className =
    'w-8 h-8 flex-shrink-0 rounded-full flex items-center justify-center font-bold text-white';
  if (isUser) {
    avatarDiv.classList.add('bg-blue-600');
    avatarDiv.textContent = 'A';
  } else {
    avatarDiv.classList.add('bg-teal-600');
    avatarDiv.innerHTML = `<svg class="w-6 h-6" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" d="M3.75 13.5l10.5-11.25L12 10.5h8.25L9.75 21.75 12 13.5H3.75z" /></svg>`;
  }

  const textDiv = document.createElement('div');
  // Using a simple div instead of prose for more control
  textDiv.className = 'text-gray-200 pt-0.5 leading-relaxed';
  textDiv.textContent = text;

  messageDiv.appendChild(avatarDiv);
  messageDiv.appendChild(textDiv);
  messageWrapper.appendChild(messageDiv);
  chatContainer.appendChild(messageWrapper);

  chatContainer.parentElement.scrollTop =
    chatContainer.parentElement.scrollHeight;
}

function autoResizeTextarea() {
  chatInput.style.height = 'auto';
  // Set a max-height to prevent infinite growth
  const maxHeight = 200;
  const newHeight = Math.min(chatInput.scrollHeight, maxHeight);
  chatInput.style.height = newHeight + 'px';
}

function handleKeydown(e) {
  if (e.key === 'Enter' && !e.shiftKey) {
    e.preventDefault();
    chatForm.requestSubmit();
  }
}

Final Output:

create-chatgpt-ui-clone-with-html-css-and-javascript.gif

Conclusion:

You just learned how to create a simple ChatGPT UI clone using HTML, CSS, and JavaScript. This project helps you practice:

  • Layout building with Tailwind
  • DOM interaction using JavaScript
  • Creating a clean, responsive UI

You can expand this project further by:

  • Adding real AI replies via OpenAI API
  • Storing chat history in local storage
  • Making it mobile-friendly

That’s a wrap!

I hope you enjoyed this post. Now, with these examples, you can create your own amazing page.

Did you like it? Let me know in the comments below 🔥 and you can support me by buying me a coffee

And don’t forget to sign up to our email newsletter so you can get useful content like this sent right to your inbox!

Thanks!
Faraz 😊

End of the article

Subscribe to my Newsletter

Get the latest posts delivered right to your inbox


Latest Components

Please allow ads on our site🥺