WhatsApp Clone UI | Responsive Chat App Using Tailwind CSS, HTML, JS

Build a fully responsive WhatsApp Web Clone using HTML, Tailwind CSS & JavaScript. Realistic chat UI with dynamic message rendering, file sharing, and responsive layout.

Sunday, June 8, 2025
WhatsApp Clone UI | Responsive Chat App Using Tailwind CSS, HTML, JS

WhatsApp Clone – Realistic Chat App UI with Tailwind CSS, HTML & JavaScript

Are you passionate about front-end development or looking to strengthen your UI/UX skills by building real-world projects? Presenting a fully responsive WhatsApp Clone — a modern web-based chat interface that mimics the official WhatsApp Web experience. Developed using HTML, Tailwind CSS, and JavaScript, this project offers a hands-on approach to understanding how modern chat apps work, right from UI rendering to user interaction.


🧠 What This Project Offers

This WhatsApp Clone is not just a UI demo — it’s a feature-rich front-end application that demonstrates how today’s popular messaging platforms are structured visually. Built with simplicity, performance, and clarity in mind, this project helps developers and learners dive deep into:

  • UI State Management

  • Dynamic DOM Rendering

  • Responsive Web Design

  • User Interaction Flow


🔍 Key Features of the WhatsApp Clone

1. 🗂️ Dynamic Chat List

The left panel contains a searchable and scrollable list of recent chats. Each chat card showcases:

  • Profile image

  • Last message preview

  • Timestamp

  • Unread message badge
    All elements are updated dynamically to simulate real conversation flow.

2. 💬 Interactive Chat Window

The central section is a fully interactive message window with:

  • Real-time message rendering

  • Sent and received message bubbles

  • Timestamps and read receipts

  • Date separators for better readability
    The layout and flow closely resemble WhatsApp Web, making the user experience intuitive and familiar.

3. 📁 File Sharing UI

You can visually explore how shared files appear in a WhatsApp-like environment. This includes:

  • File name and preview

  • Download button

  • Display card layout
    This feature prepares the ground for backend integration in future versions.

4. 📱 Fully Responsive Design

Thanks to Tailwind CSS, the app scales beautifully across screen sizes. Whether you’re on a mobile, tablet, or desktop, the layout adapts perfectly, offering a consistent user experience across devices.

5. 🔄 Smooth User Interaction

From selecting chats to typing messages, every interaction is smooth and responsive. Event handling and conditional rendering make the interface feel alive, much like the real WhatsApp.


💡 Why Build This?

This project is perfect for:

  • Front-end developers looking to master responsive UI design

  • Students who want to build portfolio-worthy projects

  • JavaScript learners interested in event handling and DOM manipulation

  • Professionals aiming to prototype real-time applications


🚀 What You’ll Learn

By exploring or customizing this project, you’ll gain practical knowledge of:

  • Modern web UI principles

  • Managing UI states without frameworks

  • Working with responsive utilities in Tailwind CSS

  • Structuring reusable and maintainable front-end code

This project can also act as the front-end layer for a complete full-stack chat application when connected to real-time databases like Firebase or Socket.io.


🔗 Source Code & Demo

You can view the live demo and access the full source code from the link below:

HTML Code

  <div class="flex h-screen">
   <!-- Left Sidebar -->
   <aside class="w-72 bg-[#1e1e1e] flex flex-col border-r border-[#2a2a2a]">
    <!-- Header -->
    <header class="flex items-center justify-between px-4 py-3 border-b border-[#2a2a2a]">
     <div class="flex items-center space-x-2">
      <i class="fab fa-whatsapp text-[#25d366] text-xl">
      </i>
      <span class="text-white font-semibold text-lg">
       WhatsApp
      </span>
     </div>
     <div class="flex space-x-3 text-gray-400 text-lg">
      <button aria-label="New chat" class="hover:text-white" id="newChatBtn">
       <i class="fas fa-edit">
       </i>
      </button>
      <button aria-label="Menu" class="hover:text-white">
       <i class="fas fa-bars">
       </i>
      </button>
     </div>
    </header>
    <!-- Tabs -->
    <nav class="flex flex-col space-y-4 px-2 py-3 border-b border-[#2a2a2a]">
     <button aria-label="Chats" class="text-[#25d366] border-l-4 border-[#25d366] pl-3 flex items-center space-x-2">
      <i class="fas fa-comment-alt text-[#25d366]">
      </i>
      <span class="font-bold text-sm">
       Chats
      </span>
     </button>
     <button aria-label="Calls" class="text-gray-400 hover:text-white flex items-center space-x-2 pl-3">
      <i class="fas fa-phone">
      </i>
      <span class="text-sm">
       Calls
      </span>
     </button>
     <button aria-label="Status" class="text-gray-400 hover:text-white flex items-center space-x-2 pl-3">
      <i class="fas fa-circle-notch">
      </i>
      <span class="text-sm">
       Status
      </span>
     </button>
    </nav>
    <!-- Search -->
    <div class="px-4 py-2">
     <input class="w-full bg-[#2a2a2a] rounded-md text-gray-300 placeholder-gray-500 text-sm px-3 py-2 focus:outline-none focus:ring-1 focus:ring-[#25d366]" id="searchInput" placeholder="Search or start a new chat" type="text"/>
    </div>
    <!-- Chats List -->
    <div class="flex-1 overflow-y-auto scrollbar-thin scrollbar-thumb-[#2a2a2a] scrollbar-track-transparent" id="chatList">
     <ul class="divide-y divide-[#2a2a2a]" id="chatListUl">
      <!-- Chat items will be rendered here by JS -->
     </ul>
    </div>
    <!-- Bottom icons -->
     <center>
      <div class="flex flex-row items-center space-x-4 py-4 border-t border-[#2a2a2a]">
  <button aria-label="Starred" class="text-gray-400 hover:text-white text-lg">
    <i class="fas fa-star"></i>
  </button>
  <button aria-label="Trash" class="text-gray-400 hover:text-white text-lg">
    <i class="fas fa-trash"></i>
  </button>
  <button aria-label="Settings" class="text-gray-400 hover:text-white text-lg">
    <i class="fas fa-cog"></i>
  </button>
  <button aria-label="Profile" class="text-gray-400 hover:text-white text-lg">
    <i class="fas fa-user-circle"></i>
  </button>
</div>

     </center>

   </aside>
   <!-- Main Chat Area -->
   <main class="flex-1 flex flex-col bg-[#121212] relative">
    <!-- Chat header -->
    <header class="flex items-center justify-between px-4 py-3 border-b border-[#2a2a2a]" id="chatHeader" style="min-height:56px;">
     <div class="flex items-center space-x-3">
      <img alt="Profile picture" class="rounded-full w-10 h-10 object-cover" height="40" id="chatHeaderImg" src="" width="40"/>
      <h1 class="text-white font-semibold text-sm truncate max-w-xs" id="chatHeaderName">
       Select a chat
      </h1>
     </div>
     <div class="flex items-center space-x-3 text-gray-400 text-lg" id="chatHeaderActions" style="visibility:hidden;">
      <button aria-label="Video call" class="hover:text-white" id="videoCallBtn">
       <i class="fas fa-video">
       </i>
      </button>
      <button aria-label="Voice call" class="hover:text-white" id="voiceCallBtn">
       <i class="fas fa-phone">
       </i>
      </button>
      <button aria-label="Search" class="hover:text-white" id="searchChatBtn">
       <i class="fas fa-search">
       </i>
      </button>
     </div>
    </header>
    <!-- Chat messages -->
    <section class="flex-1 overflow-y-auto px-6 py-4 space-y-6 scrollbar-thin scrollbar-thumb-[#2a2a2a] scrollbar-track-transparent bg-[url('https://i.ibb.co/7QpKsCX/whatsapp-bg.png')] bg-repeat" id="chatMessages" style="background-size: 60px 60px;">
     <div class="flex justify-center" id="dateLabel" style="display:none;">
      <span class="bg-[#1a1a1a] text-gray-400 text-xs px-2 py-0.5 rounded" id="dateLabelText">
      </span>
     </div>
     <div id="messagesContainer" class="space-y-3 max-w-full">
      <!-- Messages will be rendered here -->
     </div>
    </section>
    <!-- Message input -->
    <form class="flex items-center px-4 py-3 border-t border-[#2a2a2a]" id="messageForm">
     <button aria-label="Attach" class="text-gray-400 hover:text-white text-lg mr-3" type="button" id="attachBtn">
      <i class="fas fa-paperclip">
      </i>
     </button>
     <input autocomplete="off" class="flex-1 bg-[#2a2a2a] rounded-full text-gray-300 placeholder-gray-500 text-sm px-4 py-2 focus:outline-none focus:ring-1 focus:ring-[#25d366]" id="messageInput" placeholder="Type a message" type="text"/>
     <button aria-label="Send" class="text-gray-400 hover:text-white text-lg ml-3" type="submit" id="sendBtn">
      <i class="fas fa-paper-plane">
      </i>
     </button>
    </form>
   </main>
  </div>

  <script src="https://cdn.tailwindcss.com">
  </script>
  <link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.3/css/all.min.css" rel="stylesheet"/>

CSS Code

 
   /* Custom scrollbar for chat list */
    ::-webkit-scrollbar {
      width: 6px;
      height: 6px;
    }
    ::-webkit-scrollbar-thumb {
      background-color: #2a2a2a;
      border-radius: 3px;
    }
    ::-webkit-scrollbar-track {
      background: transparent;
    }
    /* Hide number input arrows */
    input[type="number"]::-webkit-inner-spin-button,
    input[type="number"]::-webkit-outer-spin-button {
      -webkit-appearance: none;
      margin: 0;
    }
  

JavaScript Code

 
   // Data for chats and messages
const chats = [
  {
    id: 1,
    name: "Rahul Gurugram Pg209",
    lastMessage: "Voice call · Accepted on another dev…",
    lastMessageTime: "Yesterday",
    unreadCount: 0,
    avatar: null,
    messages: [
      { id: 1, text: "Voice call accepted on another device", time: "Yesterday", fromMe: false, type: "text", date: "2025-06-04" }
    ],
  },
  {
    id: 2,
    name: "Abhishek New Opjs",
    lastMessage: "Image",
    lastMessageTime: "Yesterday",
    unreadCount: 0,
    avatar: "https://storage.googleapis.com/a1aa/image/6032d1e5-0cd2-42d0-af7f-01a9ff93a9a5.jpg",
    messages: [
      { id: 1, text: "Image", time: "Yesterday", fromMe: false, type: "text", date: "2025-06-04" }
    ],
  },
  {
    id: 3,
    name: "VACANCY GROUP KONDAGAON...",
    lastMessage: "~Satendar Netam😍🥰 Image",
    lastMessageTime: "Yesterday",
    unreadCount: 82,
    avatar: "https://storage.googleapis.com/a1aa/image/c863f19b-9fdb-4779-7861-2bb0d6e4ea4f.jpg",
    messages: [
      { id: 1, text: "~Satendar Netam😍🥰 Image", time: "Yesterday", fromMe: false, type: "text", date: "2025-06-04" }
    ],
  },
  {
    id: 4,
    name: "JioAICloud",
    lastMessage: "All-New JioAICloud is here! Live In…",
    lastMessageTime: "Yesterday",
    unreadCount: 1,
    avatar: "https://storage.googleapis.com/a1aa/image/f98151c6-12f4-47be-c929-0e8ac68092db.jpg",
    messages: [
      { id: 1, text: "All-New JioAICloud is here! Live In…", time: "Yesterday", fromMe: false, type: "text", date: "2025-06-04" }
    ],
  },
  {
    id: 5,
    name: "Adsense Buy & Sell",
    lastMessage: "~Rahul Kumar left",
    lastMessageTime: "3:00 pm",
    unreadCount: 0,
    avatar: "https://storage.googleapis.com/a1aa/image/3137f0a7-11cf-44b7-15fe-8a0155ed7220.jpg",
    messages: [
      { id: 1, text: "~Rahul Kumar left", time: "3:00 pm", fromMe: false, type: "text", date: "2025-06-04" }
    ],
  },
  {
    id: 6,
    name: "MICROSOFT TRAINING AN...",
    lastMessage: "~Diya: STUDENTS WHO HAVE BEEN S…",
    lastMessageTime: "06/06/2025",
    unreadCount: 8,
    avatar: "https://storage.googleapis.com/a1aa/image/6e0f2990-62f6-4f45-2b99-d6205625dd0d.jpg",
    messages: [
      { id: 1, text: "~Diya: STUDENTS WHO HAVE BEEN S…", time: "06/06/2025", fromMe: false, type: "text", date: "2025-06-06" }
    ],
  },
  {
    id: 7,
    name: "🥂 छत्तीसगढ़ मराठा🥂",
    lastMessage: "~Nitish Kumar 😊 ☺️ This message…",
    lastMessageTime: "06/06/2025",
    unreadCount: 168,
    avatar: "https://storage.googleapis.com/a1aa/image/bc8fad23-d4b9-450e-b068-ae9f599ec340.jpg",
    messages: [
      { id: 1, text: "~Nitish Kumar 😊 ☺️ This message…", time: "06/06/2025", fromMe: false, type: "text", date: "2025-06-06" }
    ],
  },
  {
    id: 8,
    name: "Kunal Punia Gurgaon University",
    lastMessage: "✓✓ Tu kar va liya??",
    lastMessageTime: "06/06/2025",
    unreadCount: 0,
    avatar: "https://storage.googleapis.com/a1aa/image/ef6b136a-c7bc-4355-0255-1eeb831f94b8.jpg",
    messages: [
      { id: 1, text: "ok", time: "6:31 am", fromMe: false, type: "text", date: "2025-06-05" },
      { id: 2, text: "Hmm, bag may hai phone\nAa raha hai", time: "6:31 am", fromMe: true, type: "text", date: "2025-06-05" },
      { id: 3, text: "Noc", time: "12:18 pm", fromMe: false, type: "text", date: "2025-06-06" },
      { id: 4, text: "Sign kerva le", time: "12:20 pm", fromMe: false, type: "text", date: "2025-06-06" },
      { id: 5, text: "aman_Resume.pdf", time: "9:45 am", fromMe: true, type: "file", date: "2025-06-06", file: {
        name: "aman_Resume.pdf",
        size: "233 KB",
        description: "Firefox PDF Document",
        preview: "https://storage.googleapis.com/a1aa/image/7431d12a-e980-4a45-4fb7-059b75511589.jpg",
        icon: "https://storage.googleapis.com/a1aa/image/56c2693c-5bd3-411a-09df-d29721bf920b.jpg",
        downloadUrl: "#"
      }},
      { id: 6, text: "Tu kar va liya??", time: "12:31 pm", fromMe: true, type: "text", date: "2025-06-06" }
    ],
  },
  {
    id: 9,
    name: "BRAND HUT SPORTS WEAR",
    lastMessage: "~जय जय सिया राम 🚩🚩🚩🚩 Image",
    lastMessageTime: "06/06/2025",
    unreadCount: 126,
    avatar: "https://storage.googleapis.com/a1aa/image/8fccce80-0a65-4b74-052c-2243c197288e.jpg",
    messages: [
      { id: 1, text: "~जय जय सिया राम 🚩🚩🚩🚩 Image", time: "06/06/2025", fromMe: false, type: "text", date: "2025-06-06" }
    ],
  },
  {
    id: 10,
    name: "Chhattisgarh sports 🏅🏅...",
    lastMessage: "Kasim Bhai Xmxtbbs",
    lastMessageTime: "06/06/2025",
    unreadCount: 59,
    avatar: "https://storage.googleapis.com/a1aa/image/f5828e68-6a4d-4c27-d900-2adcd97bb36e.jpg",
    messages: [
      { id: 1, text: "Kasim Bhai Xmxtbbs", time: "06/06/2025", fromMe: false, type: "text", date: "2025-06-06" }
    ],
  },
];

// State
let selectedChatId = null;

// Elements
const chatListUl = document.getElementById("chatListUl");
const chatHeaderName = document.getElementById("chatHeaderName");
const chatHeaderImg = document.getElementById("chatHeaderImg");
const chatHeaderActions = document.getElementById("chatHeaderActions");
const messagesContainer = document.getElementById("messagesContainer");
const dateLabel = document.getElementById("dateLabel");
const dateLabelText = document.getElementById("dateLabelText");
const messageForm = document.getElementById("messageForm");
const messageInput = document.getElementById("messageInput");
const searchInput = document.getElementById("searchInput");

// Utility: format date label
function formatDateLabel(dateStr) {
  const date = new Date(dateStr);
  const today = new Date();
  const yesterday = new Date(today);
  yesterday.setDate(today.getDate() - 1);

  if (
    date.getDate() === today.getDate() &&
    date.getMonth() === today.getMonth() &&
    date.getFullYear() === today.getFullYear()
  ) {
    return "Today";
  }
  if (
    date.getDate() === yesterday.getDate() &&
    date.getMonth() === yesterday.getMonth() &&
    date.getFullYear() === yesterday.getFullYear()
  ) {
    return "Yesterday";
  }
  // Return weekday name
  return date.toLocaleDateString(undefined, { weekday: "long" });
}

// Render chat list
function renderChatList(filter = "") {
  chatListUl.innerHTML = "";
  const filteredChats = chats.filter((chat) =>
    chat.name.toLowerCase().includes(filter.toLowerCase())
  );
  filteredChats.forEach((chat) => {
    const li = document.createElement("li");
    li.className =
      "flex items-center px-4 py-3 cursor-pointer hover:bg-[#2a2a2a]" +
      (chat.id === selectedChatId ? " bg-[#2a2a2a] border-l-4 border-[#25d366]" : "");
    li.dataset.chatId = chat.id;

    // Avatar
    let avatarEl;
    if (chat.avatar) {
      avatarEl = document.createElement("img");
      avatarEl.src = chat.avatar;
      avatarEl.alt = `Profile picture of ${chat.name}`;
      avatarEl.className = "rounded-full w-10 h-10 object-cover";
      avatarEl.width = 40;
      avatarEl.height = 40;
    } else {
      avatarEl = document.createElement("div");
      avatarEl.className =
        "flex-shrink-0 rounded-full bg-gray-600 w-10 h-10 flex items-center justify-center text-gray-400 text-sm font-semibold";
      avatarEl.textContent = chat.name.charAt(0);
    }
    li.appendChild(avatarEl);

    // Info container
    const infoDiv = document.createElement("div");
    infoDiv.className = "ml-3 flex-1 min-w-0";

    // Top row: name and time
    const topRow = document.createElement("div");
    topRow.className = "flex justify-between items-center";

    const nameP = document.createElement("p");
    nameP.className = "text-white font-semibold text-sm truncate";
    if (chat.name.length > 25) {
      nameP.className = "text-white font-semibold text-xs truncate font-bold";
    }
    nameP.textContent = chat.name;
    topRow.appendChild(nameP);

    const timeP = document.createElement("p");
    timeP.className = chat.unreadCount > 0 ? "text-[#25d366] text-xs" : "text-gray-400 text-xs";
    timeP.textContent = chat.lastMessageTime;
    topRow.appendChild(timeP);

    infoDiv.appendChild(topRow);

    // Bottom row: last message and unread count
    const bottomRow = document.createElement("div");
    bottomRow.className = "flex items-center space-x-1 text-gray-400 text-xs truncate";

    // For last message, parse icons and text
    if (chat.lastMessage.includes("Voice call")) {
      const icon = document.createElement("i");
      icon.className = "fas fa-phone-alt text-xs";
      bottomRow.appendChild(icon);
      const span = document.createElement("span");
      span.textContent = " " + chat.lastMessage.replace("Voice call", "").trim();
      bottomRow.appendChild(span);
    } else if (chat.lastMessage.includes("Image")) {
      if (chat.lastMessage.includes("Image")) {
        const textPart = chat.lastMessage.replace("Image", "").trim();
        if (textPart) {
          const span = document.createElement("span");
          span.textContent = textPart;
          bottomRow.appendChild(span);
        }
        const icon = document.createElement("i");
        icon.className = "far fa-image text-xs";
        bottomRow.appendChild(icon);
        const span2 = document.createElement("span");
        span2.textContent = " Image";
        bottomRow.appendChild(span2);
      } else {
        const span = document.createElement("span");
        span.textContent = chat.lastMessage;
        bottomRow.appendChild(span);
      }
    } else {
      const span = document.createElement("span");
      span.textContent = chat.lastMessage;
      bottomRow.appendChild(span);
    }

    infoDiv.appendChild(bottomRow);

    li.appendChild(infoDiv);

    // Unread count badge
    if (chat.unreadCount > 0) {
      const badge = document.createElement("div");
      badge.className = "ml-2";
      const badgeSpan = document.createElement("span");
      badgeSpan.className =
        "bg-[#25d366] text-black text-xs font-semibold rounded-full px-2 py-0.5";
      badgeSpan.textContent = chat.unreadCount;
      badge.appendChild(badgeSpan);
      li.appendChild(badge);
    }

    li.addEventListener("click", () => {
      selectChat(chat.id);
    });

    chatListUl.appendChild(li);
  });
}

// Render messages for selected chat
function renderMessages(chat) {
  messagesContainer.innerHTML = "";
  if (!chat) return;

  let lastDate = null;

  chat.messages.forEach((msg) => {
    // Show date label if date changes
    if (msg.date !== lastDate) {
      lastDate = msg.date;
      const dateDiv = document.createElement("div");
      dateDiv.className = "flex justify-center";
      const dateSpan = document.createElement("span");
      dateSpan.className = "bg-[#1a1a1a] text-gray-400 text-xs px-2 py-0.5 rounded";
      dateSpan.textContent = formatDateLabel(msg.date);
      dateDiv.appendChild(dateSpan);
      messagesContainer.appendChild(dateDiv);
    }

    // Message container
    const msgDiv = document.createElement("div");
    msgDiv.className = "flex max-w-xs " + (msg.fromMe ? "justify-end ml-auto" : "justify-start");
    msgDiv.style.wordBreak = "break-word";

    // Message bubble
    const bubble = document.createElement("div");
    bubble.className =
      (msg.fromMe ? "bg-[#075e54] text-white" : "bg-[#2a2a2a] text-gray-400") +
      " text-xs rounded px-2 py-1 whitespace-pre-wrap";

    if (msg.type === "text") {
      bubble.textContent = msg.text;
    } else if (msg.type === "file" && msg.file) {
      // File message structure
      bubble.className = (msg.fromMe ? "bg-[#075e54]" : "bg-[#2a2a2a]") + " rounded border-2 border-[#075e54] w-full max-w-[280px]";
      bubble.innerHTML = `
        <img src="${msg.file.preview}" alt="Blurred preview of a resume document with green border" class="rounded-t w-full max-h-[140px] object-cover" />
        <div class="px-3 py-2 border-t border-[#064d40]">
          <div class="flex items-center space-x-2 mb-1">
            <img src="${msg.file.icon}" alt="PDF icon" class="w-5 h-5 object-contain" />
            <div class="flex flex-col text-white text-xs truncate">
              <span class="font-semibold truncate">${msg.file.name}</span>
              <span class="text-gray-300">${msg.file.size}, ${msg.file.description}</span>
            </div>
          </div>
          <button class="w-full bg-[#064d40] hover:bg-[#0a6a4a] text-white text-xs rounded py-1 text-center" type="button" onclick="alert('Downloading ${msg.file.name}')">Download</button>
        </div>
        <div class="text-gray-300 text-[10px] text-right px-2 py-1 select-text">${msg.time} <i class="fas fa-check-double"></i></div>
      `;
      // Append and return early to avoid adding time again below
      messagesContainer.appendChild(bubble);
      return;
    }

    // Time and double check icon for sent messages
    const timeSpan = document.createElement("span");
    timeSpan.className = msg.fromMe ? "text-[#25d366] text-[10px] self-end ml-1" : "text-gray-500 text-[10px] self-end ml-1";
    timeSpan.style.alignSelf = "flex-end";
    timeSpan.style.marginLeft = "0.25rem";
    timeSpan.textContent = msg.time;
    if (msg.fromMe) {
      const checkIcon = document.createElement("i");
      checkIcon.className = "fas fa-check-double ml-1";
      timeSpan.appendChild(checkIcon);
    }

    msgDiv.appendChild(bubble);
    msgDiv.appendChild(timeSpan);
    messagesContainer.appendChild(msgDiv);
  });
}

// Select chat by id
function selectChat(id) {
  selectedChatId = id;
  renderChatList(searchInput.value);
  const chat = chats.find((c) => c.id === id);
  if (!chat) return;
  chatHeaderName.textContent = chat.name;
  chatHeaderImg.src = chat.avatar || "https://placehold.co/40x40?text=NA&bg=6b7280&fg=fff";
  chatHeaderImg.alt = `Profile picture of ${chat.name}`;
  chatHeaderActions.style.visibility = "visible";
  renderMessages(chat);
  scrollMessagesToBottom();
  messageInput.focus();
}

// Scroll messages container to bottom
function scrollMessagesToBottom() {
  const chatMessages = document.getElementById("chatMessages");
  chatMessages.scrollTop = chatMessages.scrollHeight;
}

// Send message handler
messageForm.addEventListener("submit", (e) => {
  e.preventDefault();
  const text = messageInput.value.trim();
  if (!text || selectedChatId === null) return;
  const chat = chats.find((c) => c.id === selectedChatId);
  if (!chat) return;

  // Add new message
  const now = new Date();
  const timeStr = now.toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" });
  const dateStr = now.toISOString().split("T")[0];
  chat.messages.push({
    id: chat.messages.length + 1,
    text,
    time: timeStr,
    fromMe: true,
    type: "text",
    date: dateStr,
  });
  chat.lastMessage = text;
  chat.lastMessageTime = timeStr;
  chat.unreadCount = 0;

  messageInput.value = "";
  renderChatList(searchInput.value);
  renderMessages(chat);
  scrollMessagesToBottom();
});

// Search input handler
searchInput.addEventListener("input", () => {
  renderChatList(searchInput.value);
});

// Initial render
renderChatList();

// Select default chat (Kunal Punia Gurgaon University)
selectChat(8);
  

PHP Code

NO Need

🧩 Tech Stack Used

  • HTML5 – Semantic structure and layout

  • Tailwind CSS – Utility-first styling for responsiveness

  • JavaScript (Vanilla) – Handling dynamic behavior and DOM updates


📈 Final Thoughts

This WhatsApp Clone project proves how powerful and flexible modern web technologies can be — even without using heavy frameworks. It serves as an excellent starting point for building full-fledged messaging platforms, UI prototypes, or simply sharpening your front-end skills.

If you’re passionate about building real-world UI interfaces that not only look good but also feel interactive — this is the project for you.


❓FAQs

Q1. Is this WhatsApp Clone connected to any backend?
No, this is a front-end UI-only template. However, it's designed to be easily connected to real-time backends like Firebase or Socket.io.

Q2. Is this WhatsApp clone responsive?
Yes, the entire interface is mobile-first and adapts perfectly across devices using Tailwind CSS.

Q3. Can I customize this template?
Absolutely. The code is well-commented and modular, making it beginner-friendly and easy to customize.

Q4. What makes this a good portfolio project?
It demonstrates real-world UI skills, responsive design, event handling, and interactive elements — all essential in modern web development.







Leave a Comment: 👇