https://github.com/yyq1025/twitter-clone-2026
https://github.com/bluesky-social/social-app
High-frequency social interactions (like/repost/follow/bookmark) are judged by latency and coherence, not raw throughput. The naïve pattern—“send mutation → invalidate caches → refetch”—is “correct” but often the wrong default, especially for timelines: in systems like Bluesky, the timeline is effectively a recommendation / non-deterministic feed, so a refetch can return different items and different ordering, causing list instability, flicker, and confusing UX. My current project doesn’t ship a recommendation timeline yet, but I treat “no refetch” as a first-class constraint because it becomes the only safe baseline once timelines become non-deterministic.
Bluesky avoids the mutation→refetch loop with shadows: keep server snapshots stable, record viewer intent in a lightweight overlay (PostShadow), and render UI as snapshot + overlay (counts are often displayed as snapshot ± 1 for my action). Shadows live in a WeakMap<PostView, Partial<PostShadow>>, and Bluesky propagates updates across all in-memory copies of the same post by scanning keys and matching postId.
In my stack, I aim for the same UX (instant feedback, no refetch) but without a permanent projection layer: I treat interactions as real facts inside TanStack DB collections, synced and confirmed via ElectricSQL. Concretely, viewer state like “have I liked/bookmarked/followed?” becomes a fast membership check in user-scoped subsets, and counters are updated in the same optimistic transaction as the fact write. For confirmation, I await the collection txid (awaitTxId) and let the server be authoritative—so optimistic UI becomes “real” once the mutation is observed in the sync stream, without refetching volatile timelines (per the no-refetch rule: patch rendered items, don’t recompute the list).
If you don’t have sync + confirmation, shadows are a strong default. If you do, modeling interactions as facts (with transactional confirmation) usually removes the need for a long-lived projection layer.
This article is about the latency-sensitive core of social UX: viewer interactions (like/unlike, repost/unrepost, follow/unfollow, bookmark/unbookmark) plus counters users check immediately after tapping.
These operations look trivial but are hard because they must satisfy three constraints simultaneously:
“Done for real” means: instant UI backed by state maintained as data—not only as a render-time illusion over stale snapshots—and with a clear path to confirmation (or rollback) that doesn’t rely on list-level refetch.
I use like/unlike as the main case study because it exercises the full problem surface:
viewerHasLiked)likeCount)