Skip to content

Track upstream rrweb changes to pull in #3765

Description

@pauldambra

Track upstream rrweb changes to pull in

We vendor a fork of rrweb-io/rrweb under packages/rrweb/* (moved into this monorepo from the retired PostHog/posthog-rrweb in #3510). This issue tracks upstream changes worth pulling into our fork, and replaces the old split tracking in PostHog/posthog-rrweb#155 (open PRs) and #147 (merged PRs), which are now stranded in the old repo.

Fork point: upstream 2.0.0-alpha.18 (2024-12-06).
Upstream now: 2.1.5 (2026-09-17). 2.1.5 contains #1652, a recording-time mutation-processing performance refactor. It is a should pull in item: our fork still uses the older secondary add-list path and lacks the corresponding accessor helpers. The other 2.1.5 package releases are version/dependency-only for this assessment. Adoption in flight: #5010. Upstream released 2.1.2, 2.1.3 and 2.1.4 on the same day. Release by release since our fork point: 2.0.0 mostly re-published pre-alpha.18 PRs we already have; 2.0.1 added one fix (#1854); 2.1.0 added #1864 + #1865; 2.1.1 has no code changes (version bumps only); 2.1.2 added #1712 + #1769; 2.1.3 added #1921 + #1771; 2.1.4 has no code changes (version bumps only, plus the #1904 console-plugin fix in that package).

2.1.3 is the first release since our fork point that contains a fix we both need and do not have. All the same, the release train is still not where the interesting changes are — the genuinely-new surface is PRs merged after 2024-12-06 plus the currently-open PRs.

Prioritisation lens: recording performance (CPU/memory cost on the customer's page while recording) outranks everything else — replay fixes, masking gaps, and framework compat all queue behind it. Recording-perf items are ranked first in the adoption plan (see comments).

This is a comprehensive scan: every merged-since-fork and open PR was triaged, and each "adopt"/"already-have" call was verified by grepping our fork. Items marked * are PostHog-authored upstream PRs that aren't in our fork yet — adopting them early means vendoring our own not-yet-landed work; they're also tracked in the contribute-back issue (#3766).

Trustworthiness criteria

  • High — core maintainers (Juice10, eoghanmurray, juliecheng, YunFeng0817), PostHog authors (pauldambra, d0ugal, daibhin, megboehlert, billyvg, benjackwhite, marandaneto, kevinatown), or APPROVED PRs.
  • Medium — known/repeat contributors, or PRs with substantive review.
  • Low — unknown/community/no-review (validate before adopting).

Merged upstream since our fork — clean cherry-picks (not yet in our fork)

PR Author Trust Theme Priority Why it matters
#1769 heathdutton High replay Reconcile, don't cherry-pick Merged 2026-09-08, shipped in 2.1.2. Upstream deletes the case EventType.Custom skip outright, so every custom event re-fires on seek. Our fork already fixes the same bug more narrowly: shouldCastInSyncMode (rrweb/src/replay/index.ts:835) re-casts only the rrweb/fullscreen custom event and keeps the rest skipped. Decide deliberately whether to widen to upstream's behaviour or keep ours — and if we keep ours, it becomes a contribute-back item (#3766).
#1865 eoghanmurray High css Medium Preserve the URL #fragment when converting absolute->relative CSS urls (e.g. url(#filter)); real correctness gap in our absolutifyURLs.
#1763 wfk007 Low framework compat Low Use untainted ownerDocument so shadow-DOM detection works under wujie/micro-frontends that patch ownerDocument.
#1864 eoghanmurray High replay Low Fix getCurrentTime() stuck in liveMode after the last event; our timer.ts has diverged (rafCheck, guarded timeOffset) so this needs a careful manual port.

The 2.1.3 / 2.1.4 batch is done (2026-09-10) — #1921 via #4837, #1771 via #4861, #1904 via #4862, all merged to main. Only #1865, #1763 and #1864 remain as pending cherry-picks, and all three predate the 2.1.x releases.

#1854 is done — adopted in #4128 (merged 2026-07-15) with a deliberate divergence (page-lifetime keepalive rather than upstream's teardown removal); see utils/src/index.ts keepIframeAttached. It is no longer a pending cherry-pick.

Every other substantive merged-since-fork PR is already in our fork (verified — see below) or CI/release noise.


Open PRs — Should adopt (High)

PR Author Trust Theme Why it matters (+ review state)
#1320 eoghanmurray High performance Standout. Fixes replay 500ms-timeout stall on large dependent node trees (~200 cascading <tr>); 11 lines, we provably lack the rootNexts logic.
#1673 eoghanmurray High replay pause is not a function throw when a site (Squarespace) dispatches media events on non-media elements; cheap guard + try/catch.
#1745 jakewski Medium masking/privacy maskAllInputs doesn't mask type=hidden inputs — can leak tokens/PII. (Re-spin of #1610.)
#1934 (supersedes #1812) Juice10, from juliecheng High stability New 2026-09-08 — adopt this instead of #1812. Recovers a native Proxy from the shared untainted iframe and caches it, so sites whose libraries overwrite global Proxy (old jsPDF) do not break the stylesheet observers. Our fork calls bare new Proxy(...) in 10 places (9 in rrweb/src/record/observer.ts, 1 in rrweb/src/utils.ts) and has no getUntaintedProxy — so every one of those sites is exposed today. Maintainer-authored, carries #1812 forward, and adds tests on Chromium + WebKit. Read the packages/utils part against our #1854 divergence before porting — see the note below.
#1469 * daibhin High (PostHog) defensive Wrap createImageBitmap in callbackWrapper so bitmap errors route through the configured errorHandler instead of bubbling. (Overlaps our #3527.)
#1635 * megboehlert High (PostHog) framework compat Chrome iframes with custom web components defined in <body> never load on replay; rewrites blank-frame detection.
#1641 * megboehlert High (PostHog) css Virtual-DOM: adopted styles from removed nodes lost for surviving nodes sharing a styleId; adds a styleMap backup.
#1642 * kevinatown High (PostHog) masking/privacy Adds a blockElementFn callback for fine-grained / whitelist blocking policies.
#1686 * megboehlert High (PostHog) css Optional-chain rule?.styleSheet?.href so a missing styleSheet no longer nullifies _cssText, leaving <style> empty on replay.
#1806 * d0ugal High (PostHog) replay Fixes playback fully stopping when skipping backwards with virtual DOM (the default). Our fork still has the buggy destroyTree() ordering. Update 2026-07-11: closed unmerged upstream (by author, 2026-06-17, no supersession noted) — the fix now only exists on the closed PR branch; vendor it from there.
#1814 * megboehlert High (PostHog) stability Use untainted add/remove listener methods in on() so Zone.js-style monkey-patching doesn't break recording.

Open PRs — Should adopt (Medium)

PR Author Trust Theme Why it matters
#1164 mydea High masking/privacy Mask option/radio/checkbox values under maskAllInputs — real PII leak today. (CHANGES_REQUESTED upstream.)
#1097 mdellanoce Medium masking/privacy Make text-mask settings apply to inputs + mask <textarea> content; fixes maskInputFn missing from initial snapshot.
#1257 * billyvg High (PostHog) masking/privacy maskAttributesFn to mask/unmask arbitrary attributes. Framed as an RFC — API may still shift.
#1610 ababik Low masking/privacy Hidden-input masking (companion to #1745).
#1313 VladimirMilenko Low stability Fix "Maximum call stack exceeded" from recursive hookSetter — a real crash; 25 lines, self-contained.
#1357 jaj1014 Medium replay Styles from insertRule() lost when a <style> is moved during virtual-dom diff (we have the consumer side but not the population).
#1483 eoghanmurray High css Generalize <link rel=stylesheet> attribute-mutation handling + href updates. Hot area for us.
#1586 jaj1014 Medium css Chrome mis-serializes grid-template-areas; rebuild cssText from rule.style.
#1735 KuznetsovRoman Medium css Strip empty prop: ; declarations that cause CssSyntaxError: Unclosed string on rebuild. Sits next to our fixSafariColons.
#1428 p-mazhnik Medium canvas Canvas in iframes + shadow DOM (we cover shadow but not iframe contentWindow). APPROVED; supersedes #1413.
#1463 colingm Medium framework compat Patched methods return original toString() so frameworks (Stencil) don't detect a polyfill and break recording.
#1718 arivyiii Medium replay Fix diffChildren early-exit that drops sibling elements/styles on a node-type mismatch — real replay corruption.
#1740 eoghanmurray High replay createCDATASection throws on HTML docs; switch to a text node. (Different approach from our old empty-CDATA guard — see contribute-back.)
#1737 QuentinLowe Low recording image.currentSrc?.startsWith — currentSrc can be undefined and throw during rebuild.
#1830 eoghanmurray High replay Record canvas intrinsic dimensions even without recordCanvas:true so an unrecorded <canvas> doesn't collapse layout.
#1798 dhaval-valotia Low replay Guard media events on non-media elements (Lottie) during replay.
#1624 salomita Low stability Replayer guard: don't append a #document to a non-iframe parent (prevents whole-replay-blank crash).
#1679 econte-sprig Low security Configurable postMessage target origin instead of '*' for cross-origin-iframe recording.
#1726 ppsreejith Medium recording emitFromIframe so a middle iframe can record directly (nested-iframe case).
#558 eoghanmurray High recording Enforce weakly-ascending event timestamps so a delayed mousemove can't reorder before a click.
#1784 eoghanmurray High bundle size Tree-shake postcss out of record-only bundles (our build differs — needs adaptation).

Open PRs — Adopt-low / watch

Lower-confidence or niche; adopt opportunistically. Several are tied to features we don't currently use (assets, closed shadow DOM).

Expand (29 PRs)
PR Author Theme Note
#294 eoghanmurray framework record inside same-domain iframe via window.top; superseded conceptually by #1023
#389 eoghanmurray stability explicit destructor + optional DOM cleanup
#768 champkeh css absolutify url(...) in compact inline-style diff path (CHANGES_REQUESTED)
#960 Yuyz0112 recording count Custom events toward checkout (author says it changes behaviour)
#1023 samber framework inject a custom Window (record from any iframe)
#1140 eoghanmurray replay render full snapshot on scrub to t=0
#1212 FANCaipei masking per-selector mask-fn rules (overlaps our maskText config)
#1235 davidleon canvas canvas in shadow-DOM'd iframes (rough impl; #1428 is the cleaner superset)
#1256 BeichenPlus stability safeCrossOrigin guard for cross-origin access errors
#1300 mdellanoce performance benchmark + naive worst-case processMutations fix (pairs with #1302/#1320)
#1322 eoghanmurray css failing test only for var() shorthand serialization — this is the acceptance test for our #3542 contribute-back
#1336 ababik packer expose pako compression level
#1392 navaneethsdk css one-char regex fix so chrome-extension:// (hyphen-scheme) URLs resolve
#1439 xujiujiu recording smooth-scroll trailing-call captures stale Y (mobile); messy impl
#1465 colingm recording parent posts message to force cross-origin iframe snapshot
#1466 eoghanmurray recording test coverage for history pushState URL resolution
#1475 eoghanmurray css/perf large assets-events feature; off-main-thread stylesheet serialization
#1480 eoghanmurray css replay :hover by mutating live stylesheets (no postcss); author lists open TODOs
#1534 eoghanmurray api export isBlocked alongside rrweb.record
#1616 wfk007 performance mergeEvents util (+602 lines, no review)
#1638 mikeyagner replay handle node whose parent is missing in one mutation iteration
#1681 EricLLLLLL css avoid "Regular expression too large" on big :hover stylesheets (only if we ship that plugin)
#1733 godky css absolutifyURLs wrong for hash-route SPAs (CHANGES_REQUESTED)
#1739 eoghanmurray recording record {mode:'closed'} shadow DOM — author flags it as a privacy footgun
#1766 KristianGrafana masking optional CSP attribute on the replay iframe
#1796 alailsonko replay preserve duplicate class tokens in hoverElements for CSS attribute selectors
#1800 dhaval-valotia masking allowedIframeOrigins allowlist instead of postMessage('*')
#1239 Juice10 feature Asset event type + capture assets (large feature; CHANGES_REQUESTED) — adopt only as a deliberate feature
#1897 hi-ogawa replay new 2026-07-02: support more user-action pseudo-classes (:active, :focus, ...) on rebuild; replay-side only

New upstream PRs still open

Triaged against our fork on 2026-09-08. Items that merged into 2.1.3 / 2.1.4 have moved up to the cherry-pick table.

PR Author Trust Theme Verdict
#1934 Juice10, from juliecheng High stability Adopt (High) — supersedes #1812. See the adopt-high table above. Also touches packages/utils/src/index.ts, where we already diverge (#1854 / #4128).
#1933 eoghanmurray High recording performance Adopt (High), validate carefully. New 2026-09-08. Collapses repeat text mutations against the same node while the buffer is frozen or throttled, so only the final value is emitted — replay state is unchanged because the replayer applies every text mutation in order anyway. Also replaces the attributes array + attributeMap WeakMap pair (from #1343) with a single Map. Our fork has exactly that shape: texts: textCursor[] pushed per mutation (record/mutation.ts:170, :716) and the attributes / attributeMap pair (:171, :172). This is the same recording-CPU theme as batch 1, from a maintainer. It changes emit contents, so port it with the mutation-flood fixture and check the integration snapshots.
#1907 eoghanmurray High replay Adopt (High). Extends the DOMException guard around remove-mutations to rrdom (whose exceptions don't inherit from DOMException) and to appendChild. Our replay/index.ts:1738 still has the narrow instanceof DOMException check, so degenerate replay state can still abort a whole mutation.
#1912 carlosdanieltt Low (validate) masking/privacy Adopt (Medium). Masks placeholder on <input>/<textarea> through maskInputValue() when input masking is on. Our fork does not mask placeholders — real leak when apps build placeholders from user data. Supersedes closed #1898/#1911. Fits batch 3.
#1905 yashrao2607 Low (validate) replay Adopt (Medium). Skip srcdoc when rebuilding an iframe — the browser's own async parse of srcdoc races rrweb's reconstruction of the same document. rrdom/src/diff.ts:360 already skips srcdoc; rrweb-snapshot/src/rebuild.ts does not, so the two paths disagree.
#1927 Juice10 High canvas Adopt (Medium). Observe canvas APIs on same-origin iframe windows and serialize drawImage arguments across realms. Same gap as #1428 from the canvas side.
#1917 creasty Medium replay/css Watch. Makes the replayer's implicit hackCss rewriting of <style> text mutations configurable. Low urgency, replay-side.
#1920 creasty Medium css Not applicable. Fixes applyCssSplits/splitCssText rule-boundary splitting — our fork never vendored splitCssText (verified absent).
#1914 eoghanmurray High plugin Ignore — click-track/heatmaps plugin; posthog-js has its own heatmaps and autocapture.
#1900, #1909, #1928 packages we don't ship Ignore — popup plugin, web-extension uploads, rrweb-player annotations.
#1892, #1926 Juice10 / yashrao2607 build/metadata Ignore — plugin dependency metadata, PR package previews.

Merged upstream on 2026-09-08 and NOT relevant to us: #1903 (repository.directory metadata), #1906 + #1931 (css regression tests for the #1734/#1692 fixes — take the cases if we touch that area), #1891 / #1923 / #1924 / #1929 / #1930 / #1932 / #1935 / #1939 (CI, vitest, playwright, changelog, release, RelativeCI bundle reporting).

Closed upstream (drop from our lists): #1239 (asset events — was already "features we don't ship"), #1898 / #1911 (placeholder masking, superseded by #1912), #1895, #1656 (CI).

Note — #1934 overlaps our #1854 divergence

Read this before porting #1934. Both our fork and upstream keep the untainted-prototype iframe attached on WebKit, but the scope differs:

Upstream is right here, and it makes our version look over-broad: we can hold up to four hidden iframes on WebKit where one is enough. Take upstream's narrower condition when we port #1934, and keep our page-lifetime choice only for MutationObserver. This also weakens the "novel" claim on that row in #3766 — updated there.


Already in our fork (verified — do not re-adopt)

Confirmed present by grepping packages/rrweb. Listed so we don't re-litigate them.

Merged upstream & already vendored: #1580, #1593, #1597, #1614, #1618, #1631, #1695, #1705, #1731, #1758, #1774, #1775, #1777, #1837, #1854 (via #4128), #1712 (verified at rrweb/src/replay/index.ts:298this.warn(e); merged upstream 2026-09-04, shipped in 2.1.2), #1921 (via #4837record/mutation.ts:735 tagNameLower), #1771 (via #4861rrweb-snapshot/src/rebuild.ts:444, plus a divergence upstream lacks, see below), #1904 (via #4862plugins/rrweb-plugin-console-record/src/index.ts:198 and :240).

#1771 carries a deliberate divergence. Upstream sets autocomplete="off" at rebuild time only, so a recorded attribute mutation can set it back to a real value or remove it, re-enabling the viewer's autofill mid-replay. Our fork also skips autocomplete mutations on <input>/<textarea> in the replayer (rrweb/src/replay/index.ts:2064). That guard is not in upstream #1771 — it is tracked as a contribute-back item in #3766.
Open upstream but already in our fork (incl. our own ported PRs): #724, #1413, #1462, #1633, #1688, #1691, #1711, #1755, #1770, #1802, #1825, #1826, #1873 (via #4129), #1302 (via #4130), #1697 (via #4131). (Update 2026-07-11: #1825 and #1826 were closed unmerged upstream on 2026-06-17 — #1826 superseded by the merged #1854, #1825 superseded by equivalent maintainer work on the stylesheet-assets branch. Both remain in our fork; no action.)
Solved differently in our fork: #1791 (we have our own iframe memory-leak cleanup).


Can ignore

CI / build / release / docs / deps / version-bump noise

Merged: #1554, #1568, #1600*, #1605, #1615*, #1621, #1625, #1629, #1630, #1636, #1640, #1643, #1646, #1647, #1651, #1657, #1696, #1719, #1760, #1761, #1762, #1779, #1780, #1782, #1783, #1787, #1788, #1804, #1805, #1807, #1827, #1831, #1832, #1836, #1838, #1845, #1846, #1847, #1849, #1850, #1851, #1853, #1855, #1858, #1860, #1866.
(*#1600/#1615/#1640 fix the splitCssText feature, which our fork never vendored.)

Open: #661, #863, #1015, #1046, #1149, #1238, #1255, #1259, #1290, #1416, #1448, #1477, #1656, #1676 (empty PR), #1709, #1722, #1730, #1732, #1744, #1748, #1768, #1789, #1790, #1792, #1809, #1810, #1811, #1828, #1839, #1841, #1843, #1848, #1856, #1859, #1861, #1867, #1868.

Features we don't ship

Summary

* PostHog-authored upstream PRs are also tracked in #3766 (contribute back).

Generated from a comprehensive scan of upstream rrweb-io/rrweb on 2026-06-08; re-verified against upstream (releases + every listed PR's state) and against our fork on 2026-07-11 and again on 2026-09-08, first for 2.1.2 and then for the 2.1.3 / 2.1.4 releases; adoption state re-verified against main on 2026-09-10. The open-PR list will drift, so re-verify status before acting on an individual row.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions