Skip to content

fix(rrweb): queue insertRule events dropped due to mirror race condition#141

Draft
TueHaulund wants to merge 1 commit intomainfrom
fix/queue-dropped-insertrule-events
Draft

fix(rrweb): queue insertRule events dropped due to mirror race condition#141
TueHaulund wants to merge 1 commit intomainfrom
fix/queue-dropped-insertrule-events

Conversation

@TueHaulund
Copy link
Contributor

Summary

CSS-in-JS libraries like Emotion (used by Chakra UI v3/v4) create <style> elements and synchronously call CSSStyleSheet.insertRule() before the MutationObserver adds the node to rrweb's mirror. This causes getIdAndStyleId() to return -1 and the style rule events to be silently dropped, resulting in unstyled session replays.

This is a long-standing issue (rrweb-io/rrweb#1230, rrweb-io/rrweb#928) with no upstream fix.

How it works

  1. Queue: When insertRule is called on a sheet whose ownerNode is connected to the DOM but not yet in the mirror, queue the {rule, index} instead of silently dropping it.
  2. Retry via setTimeout(0): Schedule a single batched retry that fires after the MutationObserver microtask and any remaining synchronous insertRule calls.
  3. Deduplicate against _cssText: On retry, check mirror.getMeta(ownerNode) for _cssText. If present, the MutationObserver already serialized the full sheet state — skip the queued events. If absent, emit them as a batched StyleSheetRule callback.
  4. Cleanup: Clear the timer and queue on observer teardown.

In the common Emotion case, _cssText always captures all the initial rules, so the retry is effectively a no-op (no duplicate events). The retry acts as a safety net for edge cases where _cssText isn't set (e.g. CORS restrictions on the stylesheet).

Test plan

  • Type-check passes (pnpm turbo run check-types --filter=@posthog/rrweb)
  • All unit tests pass (89/89)
  • Manual test with Emotion/Chakra UI repro app — verify styles render correctly in replay
  • Verify no duplicate CSS rules on replay by inspecting recorded events

CSS-in-JS libraries like Emotion create <style> elements and
synchronously call insertRule() before the MutationObserver adds the
node to rrweb's mirror. This causes getIdAndStyleId() to return -1 and
the events to be silently dropped, resulting in unstyled session
replays.

Queue dropped insertRule calls for DOM-connected ownerNodes and retry
via setTimeout(0) after the MutationObserver has processed the node.
Deduplicate against _cssText to avoid emitting events already captured
by the mutation serialization.

Fixes rrweb-io/rrweb#1230
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant