[claude.ai] Web UI Stability Issues - Lag, Freezes, Crashes

Status Open
Maintainer reply None cached
Activity 11 comments · opened Dec 16, 2025

Problem

The claude.ai web interface experiences significant stability issues that degrade the user experience, particularly for heavy users on paid plans.

Issues Observed

1. UI Lag / Slow Rendering

  • Responses render slowly even on fast connections
  • Typing lags behind input in long conversations (1-2 second delay)
  • Switching between conversations hangs for several seconds

2. Mid-Conversation Freezes

  • UI becomes completely unresponsive during conversations
  • Requires waiting or force-refreshing
  • More frequent in longer conversation threads

3. Complete Page Crashes

  • Page dies entirely, requires full refresh
  • Loses any unsent message draft
  • No autosave mechanism for in-progress input

Environment

  • Browser: Firefox (latest stable)
  • OS: Linux
  • Plan: Claude Max ($200/mo)
  • Usage pattern: Heavy daily use, long conversations

Impact

  • Workflow interruption during critical tasks
  • Loss of carefully composed messages
  • Forces users to compose externally and paste in
  • Undermines confidence in the platform for important work

Proposed Solutions

  1. Implement draft autosave - Persist input field content locally, recover on crash/refresh
  2. Isolate input field rendering - Don't let conversation rendering affect input responsiveness
  3. Optimize long conversation handling - Virtualize message list, lazy load older messages
  4. Add connection state indicator - Show when connection is degraded instead of silently failing
  5. Implement graceful degradation - Queue messages when connection drops, send when restored

---

Alternatively, if these fixes are not feasible in the short term, update your ToS to allow subscribers to access their own sessions programmatically. This would let users build tools that work while these issues are addressed. Paying $200/mo for a product we can't reliably use, with no workaround permitted, is not acceptable.

View original on GitHub ↗

11 Comments

github-actions[bot] · 8 months ago

Found 3 possible duplicate issues:

  1. https://github.com/anthropics/claude-code/issues/14223
  2. https://github.com/anthropics/claude-code/issues/14226
  3. https://github.com/anthropics/claude-code/issues/14224

This issue will be automatically closed as a duplicate in 3 days.

  • If your issue is a duplicate, please close it and 👍 the existing issue instead
  • To prevent auto-closure, add a comment or 👎 this comment

🤖 Generated with Claude Code

anakod · 7 months ago

Real-world case: 1683 messages chat

I've been experiencing severe performance issues with a long-running chat. Here's what I found:

Statistics from my problematic chat:

  • Messages: 1,683
  • DOM elements: 179,011 (normal chat: ~1,500-3,000)
  • HTML size: 23.81 MB
  • Elements per message: ~108

Observed symptoms:

  • 10-30 second freezes when typing
  • perceived_ttft_ms: 34,351ms (34 sec to first token!)
  • inp_duration_ms: 6,664-17,088ms (input delay)
  • Memory usage: 877 MB

Root cause

After investigating, I found that the server sends ALL messages on every chat update:

GET /chat_conversations/{uuid}?tree=True
Response: 1683 messages, 7786 KB JSON

React receives this and re-renders the entire tree. No virtual scrolling, no pagination — just raw DOM for every single message.

Proof of concept fix

The idea is simple: intercept the API response before React gets it and trim the message array:

const originalFetch = window.fetch;

window.fetch = async (...args) => {
  const response = await originalFetch(...args);
  const url = args[0]?.url || args[0];
  
  if (url?.includes('chat_conversations') && url?.includes('tree=True')) {
    const data = await response.json();
    
    if (data.chat_messages?.length > 255) {
      data.chat_messages = data.chat_messages.slice(-255);
      // Make first message the tree root
      data.chat_messages[0].parent = null;
      data.chat_messages[0].parent_message_uuid = null;
    }
    
    return new Response(JSON.stringify(data), {
      status: response.status,
      headers: response.headers
    });
  }
  return response;
};

Result: Instant response, smooth typing, no freezes.

This is obviously a client-side workaround. The proper fix would be incremental loading (?after=last-uuid) and virtual scrolling — standard patterns that libraries like react-window solve.

---

If anyone's interested, I can share the full implementation on GitHub.

Written by Claude at user's request 🤖

anakod · 7 months ago

Update: Compression algorithm that preserves tree structure

The key insight: sort by time, take recent messages, then fix parent links so the tree stays valid.

// All messages indexed by uuid
const msgMap = new Map();
messages.forEach(m => msgMap.set(m.uuid, m));

// Sort by creation time (newest first)
const byTime = [...messages].sort((a, b) => {
  const timeA = new Date(a.created_at || a.updated_at || 0).getTime();
  const timeB = new Date(b.created_at || b.updated_at || 0).getTime();
  return timeB - timeA;
});

// Take N most recent (128 by default)
const keepCount = 128;
const recentUuids = new Set();
for (let i = 0; i < Math.min(keepCount, byTime.length); i++) {
  recentUuids.add(byTime[i].uuid);
}

// Optional: add missing parents for better context (up to 50% extra)
const maxParents = Math.floor(keepCount * 0.5);
let addedParents = 0;
let changed = true;

while (changed && addedParents < maxParents) {
  changed = false;
  for (const uuid of [...recentUuids]) {
    const msg = msgMap.get(uuid);
    if (!msg) continue;
    
    const parentUuid = msg.parent_message_uuid || msg.parent;
    if (parentUuid && !recentUuids.has(parentUuid) && msgMap.has(parentUuid)) {
      recentUuids.add(parentUuid);
      addedParents++;
      changed = true;
      if (addedParents >= maxParents) break;
    }
  }
}

// Build result, fix tree roots
const result = messages.filter(m => recentUuids.has(m.uuid));
result.forEach(m => {
  const parentUuid = m.parent_message_uuid || m.parent;
  if (parentUuid && !recentUuids.has(parentUuid)) {
    m.parent_message_uuid = null;
    m.parent = null;
  }
});

Results:

  • 852 messages → 192 messages (−77%)
  • Tree structure preserved (branches work correctly)
  • UI lag eliminated

This runs in a browser extension intercepting /api/.../chat_conversation responses before they hit the React renderer.

Still hoping for native fixes: message virtualization, draft persistence, input isolation from chat rendering.

kkumlien · 7 months ago

If it helps, claude.ai chats (even small ones) would hang for me after a short time on Firefox 146.0.1 (installed via Flatpak) on Ubuntu 24.04.3.
After _disabling_ Enhanced Tracking Protection (ETP) just for claude.ai, seems to be fine now – will update here if the issue comes back.

alldaygooning · 5 months ago
If it helps, claude.ai chats (even small ones) would hang for me after a short time on Firefox 146.0.1 (installed via Flatpak) on Ubuntu 24.04.3. After _disabling_ Enhanced Tracking Protection just for claude.ai, seems to be fine now – will update here if the issue comes back.

Thanks. It immediately fixed lags for me. Really hope that the most _ethical_ AI company of the bunch would look into not breaking their website if tracking features are disabled.

UPD: I also had to go to about:config, find privacy.resistFingerprinting (I have it enabled) and either disable it completely, or add an exception for claude.ai in privacy.resistFingerprinting.exemptedDomains

brianjlacy · 4 months ago

This is an ongoing, severe issue for me -- specifically, Claude Code on the Web (regardless of whether I'm using a browser or the app) becoming suddenly completely unresponsive in the middle of critical tasks. Sometimes it recovers minutes or even HOURS later -- other times it simply never recovers at all.

The recovery makes it even MORE frustrating -- I have no idea if I'm dead in the water and should just restart my entire coding session from scratch, or wait indefinitely to see if my work can be recovered.

This has been happening since the moment the Web UI was released -- I've been using it since day one. When it works, it works beautifully, able to complete multi step workflows without ever being bothered about permissions because it runs entirely in a sandbox. But as often as not it will fail in this horrible, unpredictable way.

gsouf · 4 months ago

for me there are frequent micro freezes of like 1 second every 5 or 10 seconds. It occurs even when there are no messages yet in the conv and occurs on both of claude (regular chat) and claude design. It's been like that for at least weeks, and freezes are only occuring on claude, no other webpage is affected.

kkumlien · 4 months ago

@brianjlacy @gsouf if on FF, have you tried the ETP and/or Fingerprinting workarounds mentioned above?

gsouf · 4 months ago

@kkumlien thanks for the heads up. It's on firefox indeed but the tips above didn't help unfortunately.

kkumlien · 4 months ago

Sorry to hear! It has been working fine for me since. FF / OS version?
Try also in "New private window" (Ctrl+Shift+P), sometimes helps.

xbaiyuan · 1 month ago

I was able to reproduce a specific, isolated trigger for the "mid-conversation freeze / complete unresponsiveness" symptom described above, on the claude.ai web app (not Claude Code CLI).

Environment: Chrome (latest stable), Linux, Claude Max plan, claude.ai/new (fresh chat, ProseMirror/Tiptap-based composer, class tiptap ProseMirror).

Root cause isolated: it's not about pasted text length — it's specifically about pasting rich/formatted HTML with many small nested inline-styled elements (the kind of clipboard payload you get copying from a webpage article, WeChat/公众号 post, Word doc, or Google Doc).

Repro via a synthetic paste ClipboardEvent dispatched on the composer's [contenteditable="true"] element (mirrors what a real OS paste delivers to the page):

  • Rich HTML paste: ~326KB HTML, 3,000 small nested <span style="..."> elements (~144,000 chars underlying text) → the tab's main thread hung for 45+ seconds (long enough to trip a 45s CDP Runtime.evaluate timeout — "renderer may be frozen or unresponsive"). No console errors were thrown; it's a pure perf/rendering hang, then it eventually recovers and shows the paste collapsed into a "pasted content" attachment chip.
  • Control — plain text paste, same/greater length: ~126,000 chars of plain text (no text/html clipboard entry) → pasted in 536ms, no hang, no issues.

This strongly suggests the bottleneck is in the paste-HTML sanitization/normalization step before it hits the editor schema (likely superlinear in the number of nodes for deeply-nested/fragmented inline-styled trees — a known perf footgun in ProseMirror-based editors when normalizing messy pasted DOM).

Workaround for users hitting this: paste as plain text (Ctrl+Shift+V / "paste without formatting") instead of a normal paste, or paste into a plain-text editor first and copy from there.

Suggested fix directions:

  1. Cap/bound the cost of HTML paste normalization (e.g. flatten/merge adjacent identically-styled inline nodes before schema parsing, or fall back to plain-text paste above a node-count threshold).
  2. Move paste sanitization off the main thread where possible, or chunk it across frames so the UI doesn't fully lock.
  3. Regardless of the fix, the input field should stay responsive during this work — even a spinner/placeholder beats a fully dead tab with no way to tell if it crashed vs. is just slow.

Happy to share the exact JS repro snippet if useful for a regression test.