Nobody Answers the Chat Bubble: Live-Chat Support for Vibe Projects
The bubble is just a button — real live chat is a conversation lifecycle: business hours and queue rules, WebSocket delivery and reconnection, the agent console, a bot safety net when nobody is online, and five groups of tests to run before launch.

The user opened the chat bubble in the corner — and nobody answered: a fictional lesson first
Here is a fictional teaching example. Chen Mo, a solo developer, used AI to build a calendar app called "Cloud Diary". In its third week after launch, a user named Lin Xiaoyu got stuck at checkout: she tapped pay, nothing happened, and the order status kept spinning. She clicked the chat bubble in the bottom-right corner of the page and wrote, "Hi, my payment failed, can someone take a look?" Then she waited. Five minutes, no reply. Forty minutes, still no reply. The bubble kept saying "Usually replies within a few minutes", but that was copy Chen Mo had borrowed from somewhere else — there was no support agent behind it, no duty roster at all. Her message went straight into a database table nobody ever looked at. The next day Lin Xiaoyu asked for a refund and left a one-star review: "Customer service is decorative."
That is the most common way live chat dies: the entrance looks convincing, but there is no complete pipeline behind it. A chat bubble is just a button. Real live chat is a whole system: who picks up after the user writes, how long the queue is, what happens when nobody is online, whether messages survive a dropped connection, and where the conversation history goes. Build only the button and you have hung a "Welcome" sign on a door with no shop assistant behind it.
This guide covers how to build live chat for a vibe project that actually works: the conversation lifecycle, business hours and queue rules, how messages get delivered and what happens when the connection drops, what the agent console should look like, what to do when nobody is online, and a pre-launch test checklist. All examples are fictional teaching examples; app names, personal names and numbers are simplified versions made up for illustration.

1. Live chat is not "an input box" — it is a conversation lifecycle
Get the concept straight first, or whatever you build will fall apart. Live chat is not "an input box plus a send button on a page". It is a complete lifecycle from birth to death: the user starts a conversation, joins a queue, gets picked up by an agent, the two sides exchange messages, the conversation is transferred to someone else when needed, it ends when the problem is solved, the user rates it, and the record is archived. Every step maps to a state, and states may only move in the directions you define — never jump around freely.
The state machine does not need to be complicated, but it must exist explicitly. The minimum workable set has six states: waiting, active, transferred, resolved, closed, and abandoned. Before an agent picks up, the conversation is waiting; after pickup it becomes active. During a transfer it is transferred, and returns to active once the new agent takes over. When the problem is solved the agent marks it resolved, and it becomes closed after the user confirms or after a timeout. If the user waits ten minutes and closes the window, the conversation becomes abandoned. Every state change needs a timestamp: when the user queued, how long they waited, when they were picked up, how long the chat lasted, when it ended. Those timestamps are the entire basis for improving support later.
The two states most often forgotten are abandoned and the step after resolved. Abandoned does not mean "the user left, end of story" — it is data you should review: how many people gave up in the queue each day, and how long they waited on average before leaving. If people who abandon waited two minutes on average, the entrance is broken — maybe users have to fill in a long form before they can even talk. If they waited fifteen minutes on average, you are short-staffed. After resolved, do not rush to close either: first ask the user "Is your problem solved?" with a "Solved / Not solved" button. If they tap "Not solved", the conversation must reopen with one tap and go back to waiting — with the original history attached, so the agent can see the full context at a glance instead of making the user describe the problem all over again.
One more detail: one user may have only one active conversation at a time. It sounds obvious, but beginners trip over it constantly: a user opens a conversation on their phone, then opens another on their laptop, and the two windows drift apart while the agent gives two different answers in each. Enforce uniqueness by user id: when the same user starts a new conversation, resume the old unclosed one and show it in place instead of creating a new one. For guests who are not logged in and have no user id, store an anonymous conversation id in the browser and keep only one conversation per browser.

2. Set the rules before you write code: business hours, queues, and concurrency limits
Before writing code, decide the support rules, put them in the help docs, and put them in the code as configuration. There are five rules, and skipping any one of them will bring someone to your door later asking about it.
Rule one is business hours. A one-person project cannot be online 24 hours a day, and saying so honestly beats pretending to be online. Business hours need three things: which days of the week you are online, what hours each day, and how holidays are handled. The interface state must follow business hours in real time: during hours it shows "Online, usually replies within X minutes"; outside hours it shows "We are currently outside business hours and will reply after 9:00 tomorrow", with a "Leave a message and we will turn it into a ticket" entry. Never show "Usually replies within a few minutes" at 3 a.m. — the user waits all night and comes back angry the next day.
Rule two is a queue cap. Queues are not infinite, so cap the number of people waiting — say, no more than 20 at once. When the queue is full, be honest: "Many people are asking right now, expected wait over 30 minutes. You can also leave a message and we will reply by email." Do not let the 21st person join the queue and wait an hour. Show the queue position live — "There are 3 people ahead of you" — and push an update every time it changes. Do not leave the user staring at a frozen page.
Rule three is the per-agent concurrency limit. How many conversations one agent handles at once must be a hard number. Start with 3: one agent holds at most 3 active conversations. Beyond 3, reply quality visibly drops — answers go to the wrong window, questions get mismatched, and user A's problem gets answered to user B. When a fourth conversation arrives, the system should either keep it waiting or route it to another online agent, never force it onto someone who is already full. Keep this limit in configuration, not hard-coded, because the day you bring in a second agent you will want to change it.
Rule four is the auto-reply. Within seconds of the user's first message, the system should answer with three facts: the message was received, how many people are ahead, and roughly how long the wait is. This is sent by the system, not typed by an agent, and it should read like a human wrote it: "Got it — there are 2 people ahead of you, an agent should pick this up within about 5 minutes. Feel free to describe the problem in the meantime." The auto-reply can also do one useful thing: ask the user to pick a problem type (payment, account, feature request, other), tag the conversation, and route it to the right person. Keep it to four options at most — more than that and users get annoyed before the conversation even starts.
Rule five is the ticket rule. Not every problem can be solved in chat: checking an account, issuing a refund, or changing data all need a ticket. The rule is: the agent clicks "Convert to ticket" in chat, and the system carries the full chat history, user info, and tags over automatically. The user sees "We have escalated this for you, ticket #1234, and we will notify you by email once it is handled." The user never has to describe the problem twice — that is the one non-negotiable part. And when the ticket is done, post the outcome back into the chat so the user sees the whole story in one place.
| Rule | Suggested value | What happens without it |
|---|---|---|
| Business hours | Days, hours, and holidays spelled out | "Replies in minutes" at 3 a.m., user waits all night |
| Queue cap | At most 20 people waiting | Person 50 waits an hour, then leaves angry |
| Per-agent concurrency | At most 3 conversations at once | Replies go to the wrong window, answers get mixed up |
| Auto-reply | Confirmation plus queue position within seconds | User cannot tell the message went through, sends it five times |
| Ticket conversion | History, tags, and user info carried over automatically | User describes the same problem again in a new place |
3. How messages get delivered: persistent connections, reconnection, and receipts
Live chat is a real-time feature, and the first principle of real-time features is: messages must not be lost, order must not scramble, and both sides must see the same state. Miss any of the three and the prettiest interface is a prop.
Start with the channel. In the browser, the standard approach is a WebSocket connection: when the user opens the chat window a connection is established and messages travel both ways over it. Do not poll — do not have the frontend ask the server every two seconds "any new messages?", because once user numbers grow, empty questions will drag the server down. The first thing after the WebSocket connects is authentication: the connection carries the login session, and the backend only allows subscribing to that user's conversation channel after verifying it. A chat channel without authentication leaves every conversation open to anyone. Never skip this step.
Then disconnections. Phones go to the background, elevators kill the signal, subways drop the connection — dropping is normal, not exceptional. The frontend must reconnect automatically: wait 1 second, then 2, then 4, then 8, with a maximum interval of 30 seconds, and each reconnection carries "the id of the last message I received". The backend uses that id to push every newer message at once — this is called catch-up after reconnect. Without it, the user comes back to find several messages missing, the agent thinks they already replied, and the user thinks the agent ignored them.
Next come receipts — the little checkmarks beside each message. Build at least three levels: sent (the message reached the server), delivered (it was pushed to the other side's device), and read (the other side opened the conversation and saw it). Receipts are not decoration; they are shared facts both sides can rely on. When the user sees "read", they know the agent saw it and will not send "are you there?" five times in a row. When the agent sees "read", they know the user saw the solution and will not keep asking "did you see it?". Throttle read receipts: send one when the user opens the conversation, not one per message. And give users a switch — some people do not want the other side to know they have read a message, so when it is off, show only up to "delivered".
Finally, ordering and de-duplication. When the network jitters, the same message can be sent twice: the user taps send, nothing seems to happen, and taps again; or a catch-up push repeats a message. De-duplication works by giving every message a client-generated unique id, with the backend treating that id as idempotent: the same id is stored once, repeats are discarded. Ordering works by giving every message a server-issued incrementing sequence number, with the frontend sorting by that number instead of arrival time. Two messages arriving at once will always sort correctly by sequence number; sorting by arrival time will eventually get one wrong, and that one will be the one the user screenshots in a complaint.
// Wrong: a failed send just disappears and the user thinks it went through
async function send(text) {
await fetch('/api/chat/send', { method: 'POST', body: text });
}
// Right: every message carries a client-side unique id, so retries are safe and repeats are dropped
async function send(text) {
const clientId = crypto.randomUUID(); // stays the same when this message is retried
const res = await fetch('/api/chat/send', {
method: 'POST',
body: JSON.stringify({ clientId, text }),
});
if (!res.ok) {
// Mark it "failed to send, tap to retry" — never silently drop it
markFailed(clientId);
}
}
Offline messages are the last safety net. When the user closes the page and the agent replies, the reply must not fall into a black hole: the backend stores it in the database, and when the user opens the chat window again, history is loaded from the database first and the live connection takes over after that. One step further: if the user left an email address and still has not come back online 10 minutes after the agent replied, the system can send an email saying "Customer support has replied to you". The email should only say "You have a new reply" — never paste the full conversation into it, because email gets forwarded and archived, while chat content should stay in the chat history.
4. The agent console: the page support opens every day decides reply quality
Many vibe projects build only the user-facing chat window and forget that agents need a console of their own. The result is that agents answer inside a makeshift admin page: the conversation list is in random order, finding one conversation takes three pages of scrolling, and replying means switching between five or six systems to look up an order. Messages written in that environment cannot be good.
The console needs at least four things. First, a conversation list grouped by "waiting to pick up", "in progress", and "resolved", with the waiting group sorted by waiting time descending — the person who waited longest sits at the top. Each row should show at a glance: who the user is, the problem-type tag, how long they have waited, and the last message. Never sort the list by creation time ascending, or the longest-waiting person sinks to the bottom while the agent always answers whoever just arrived.
Second, customer context. When an agent opens a conversation, the side panel should automatically show the user's info: when they registered, their plan, their most recent order, and past conversations. The agent should not have to open with "What is your order number?" — the system already looked it up. Context must not contain sensitive information: passwords, card numbers, and ID numbers should never appear in the agent interface at all. If it does not need to be shown, do not show it. That is a principle, not an option.
Third, canned replies. Agents type the same lines all day: "Could you share your order number?", "Please refresh the page and try again.", "Your refund has been submitted and should arrive within 3 business days." Store these as canned replies, searchable by keyword, insertable with one click. Review the canned replies regularly: which one is used most, and which one gets the most follow-up questions after it is sent. The one with the most follow-ups has a wording problem — fixing the wording beats training the agent.
Fourth, internal notes and transfers. Agents need to leave notes for each other: "This user came yesterday, the issue is the invoice title." Notes are visible only to agents, never to users. When a conversation is transferred, notes, tags, and context travel with it, so the next person does not ask the same questions again. Transfers must be visible to the user: "I am handing you over to my colleague Xiao Wang, who handles payment issues — he will take over right away." Never switch people silently and leave the user staring at a suddenly different name and tone.
The console also needs a "go on duty" switch. An agent clicks "Start duty" when work begins and the system only then routes conversations to them; clicking "End duty" hands over any unfinished conversations instead of leaving them under their name overnight. Without a duty switch, the system will route conversations at midnight to someone who is already asleep, the user waits until morning with no reply, and you still think someone is on duty.
5. The user side: the smaller the bubble, the less you can skimp on details
The user-facing chat window is usually just a small bubble in the bottom-right corner. With so little space, details matter even more. Start with the entry point: the bubble must stay visible and never hide inside a three-level menu. It should carry a status dot — green for online, grey for outside business hours — so users know before clicking whether anyone is there. It can also show an unread badge with a number, so when users come back from another page they can see at a glance that support replied.
Then the input experience. The input box should support multiple lines, pasting screenshots, and sending images and files. When users hit a problem, the most useful thing is often a screenshot of the error, not a paragraph of description. Limit image uploads: at most 5 MB per image, image formats only, compressed locally before upload. Same for files: accept common document formats only, and make sure agents can preview them directly instead of downloading something unknown.
Do not underestimate the "typing…" indicator. When the agent starts typing, the user sees "Support is typing…" and knows someone is there, instead of sending "are you there?" three times after 20 seconds of silence. It works the other way too: when the user types, the agent sees it, so the two sides do not send messages at the same moment and talk past each other. Throttle typing status: send once when typing starts, and once when the user stops for 5 seconds. Never send it on every keystroke.
The end of a conversation needs design too. When the problem is solved, do not leave the conversation hanging forever: after the agent clicks "Resolve", the user sees "Is your problem solved?" with two buttons, "Solved" and "Still a problem". After tapping "Solved", ask for a rating: 1 to 5 stars, plus an optional text box, "Anything you would like to tell us?" Ratings are not for decoration — they surface problems. Three 1-star ratings in a row pointing at the same feature mean that feature is broken. Make rating voluntary; never block the user from continuing until they rate.
Finally, privacy. Chat should never carry passwords, verification codes, or card numbers. A light reminder above the input box helps: "Please do not send passwords or verification codes in chat." When someone sends one anyway, the system should detect it and mask it — replacing a run of 6 digits or a card-number pattern with asterisks — so the agent sees "****" instead of the plain text. Write the same rule in the agent handbook: agents must never ask users for passwords or verification codes.
6. When nobody is online: let the bot hold the line, and honesty beats pretending
For a one-person project, most of the time nobody is online. Do not hide that fact — you could not hide it even if you tried. When nobody is online, the system has three jobs: let the bot hold the line first, give the user a way to reach a human, and state the promise clearly.
The bot's first layer is frequently asked questions. Turn the ten most common questions into quick entries that answer directly when tapped: "How do I get a refund?", "How do I change my plan?", "Where is my invoice?" Answers must be concrete enough to follow step by step — not a bare "Please check the help center". The second layer is keyword matching: when the user types "refund", the bot replies with the refund flow and key points; when they type "invoice", it replies with the invoice entry point. When neither layer can answer, the bot must admit it immediately: "I cannot answer this one — I have passed it to support, and an agent will reply during business hours." Admitting you cannot answer beats making something up by a hundredfold.
Leaving a message that becomes a ticket is the second safety net. Outside business hours, opening chat should show: "We are off duty now. Support will reply after 9:00 tomorrow. You can leave a message with your email and we will notify you by email when we reply." The message form needs only three things: a description of the problem, an email address, and the problem type. Do not make users fill in ten fields — they came for help, not to fill in a form. After submitting, give them a ticket number they can use to check progress.
Be honest about long queues too. If 20 people are ahead and the expected wait is 40 minutes, say so directly: "Expected wait about 40 minutes. You can also leave a message and we will reply by email." Give the choice to the user. The worst option is letting someone sit in a queue staring at a frozen "19 people ahead of you" for 40 minutes. If users choose to leave a message, they will not feel ignored; if they choose to keep waiting, they do so with expectations set.
7. Chat history and privacy: how long to keep it, who can see it, how to delete it
Chat history is the most personal thing users tell you. How long to keep it, who can see it, and how to delete it are three decisions to make before launch, written into the privacy policy.
For retention, 180 days is a good default. That is long enough to review conversations and settle disputes, without keeping years-old chats around forever. Older records should be archived or deleted automatically — do not let them pile up in the database. Deletion must be real: the database record, the backups, and any fragments left in logs all need a cleanup path. When a user asks for their chat history to be deleted, handle it within 30 days and tell them "It has been deleted."
For access, apply least privilege: only the agent handling a conversation and administrators can read it; other agents cannot. When an agent leaves or changes roles, revoke access immediately. Log every view: who looked at which conversation and when. Logging is not about distrusting agents — it is how you find out what happened when something goes wrong. Internal notes follow the same rule: visible only to the support team, never on the user side.
Mask sensitive information automatically. When users send ID numbers, card numbers, passwords, or verification codes, the system should detect them before they are stored and replace them with asterisks, storing only the masked version. The rules do not need to be fancy at first: 18 consecutive digits, 16-to-19-digit card-number patterns, and 6-digit verification codes cover the common cases. Review the masking rules regularly, because users will always find ways to send sensitive information that you did not anticipate.
One point that is easy to miss: do not use chat history to train models, and do not feed it to an AI support bot as training data, unless users have explicitly agreed. Users come to support to solve a problem, not to contribute training data. Put that in the privacy policy, not in a footnote nobody reads.
8. The pre-launch test checklist
Before launch, run through these scenarios one by one. Do not ship after testing only that "a message can be sent".
The first group is the happy path: the user starts a conversation, queues, gets picked up, exchanges three rounds of messages, the agent clicks resolve, and the user confirms it is solved and leaves a rating. Check that receipt states are correct throughout and that message order never scrambles.
The second group is disconnections: the user drops offline for 30 seconds mid-chat and comes back with no messages lost or duplicated; the agent refreshes the page mid-chat and returns to the same conversation; the user opens chat on both phone and laptop and sees one conversation, not two.
The third group is boundaries: when the queue hits its cap, the 21st person sees the "leave a message" entry instead of an endless queue; outside business hours the chat opens a message form instead of "replies within minutes"; an agent holding 3 conversations does not get a 4th forced on them; a user uploading a 10 MB image gets a clear rejection message.
The fourth group is transfers and tickets: agent A transfers to agent B, and B sees the full context; after converting to a ticket the user receives a ticket number, and when the ticket is handled the chat shows the outcome; when the user taps "Not solved", the conversation reopens with its history intact.
The fifth group is privacy: a 6-digit verification code the user sent is masked in storage; a guest who is not logged in cannot see anyone else's conversations; agent B cannot open the history of a conversation agent A is handling.
Run all five groups, then launch. In the first week after launch, watch three numbers every day: average first-response time, queue abandonment rate, and user ratings. Those three numbers are your support health score. First-response time over 10 minutes means you are short-staffed or the auto-reply is badly written. An abandonment rate over 20% means the queue cap or business hours are wrong. Ratings sliding downward means you should read the 1-star comments and find the feature they keep mentioning.
9. Common anti-patterns: mistakes others have already made
Anti-pattern one: turning the chat entry into a pop-up that covers the whole site. The user opens the homepage and the bubble pops open covering half the screen; after closing it, every refresh pops it open again. The entry should sit quietly in the corner until the user needs it. A proactive invitation is fine, but keep it restrained: show a gentle hint only after the user has stayed on the same page for over 60 seconds or failed the same action three times, and at most once per day.
Anti-pattern two: using chat for every kind of feedback. Users with feature requests, bug reports, and business inquiries are all herded into the same chat window, and agents spend the day drowning in problems that are not theirs. Chat is for "problems that need solving right now". Feature requests go to the feedback entry, business inquiries go to email — each through its own door.
Anti-pattern three: replies that are all templates. The user wrote three paragraphs describing the problem and the agent replies "Hi, how can I help you?" and then "Could you provide your order number?" Templates are fine, but read what the user said first. When the user already put the order number in their first message and the agent still asks for it, the user immediately feels that nobody is actually reading.
Anti-pattern four: shipping and then never looking at the data. Chat has been running for three months and nobody knows the average wait, how many people left mid-queue, or which problems keep recurring. You do not need much data — just three numbers: first-response time, abandonment rate, and ratings. Spend half an hour a month on them; it beats adding any feature.
Live chat comes down to one sentence: the moment a user opens the chat bubble, they assume someone is on the other side. Your job is to make that assumption true — someone picks up, picks up fast, can solve the problem, and gives a clear answer when they cannot. When the entry, the queue, the messages, the transfers, and the records all work, that small bubble is real live chat, not a decoration.
Comments (0)
Related articles

'3 minutes ago' or '2026-11-06T02:53:33.198Z'? A copy-ready threshold table, the UTC-timestamp rule, Intl rendering with tiered refresh, daylight saving and multi-language edges, plus five test groups to run before launch.

One cent off and the books won't close: why floating point can't count money, the integer-cents rule, rounding exactly once at the boundary, discounts, taxes, splitting and multi-currency traps, plus seven test cases to run before launch.

A 2 GB upload failing at 99% should not mean starting over: chunk design, slicing and concurrency, server merge, instant upload, three layers of failure handling, and a pre-launch checklist.