You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
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).
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).
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.tskeepIframeAttached. 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.
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.
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.
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 + attributeMapWeakMap 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.
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.
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.
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.
Adopt (Medium). Observe canvas APIs on same-origin iframe windows and serialize drawImage arguments across realms. Same gap as #1428 from the canvas side.
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).
Upstream main retains per key too, but registers an untaintedBaseIframeCleanup[key] remover, which we deliberately left out.
fix: dont mangle some surveys properties #1934 narrows retention to MutationObserver only, because that is the one case that needs a live ScriptExecutionContext (webkit.org/b/179224). Other constructors do not need a live context after retrieval.
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:298 — this.warn(e); merged upstream 2026-09-04, shipped in 2.1.2), #1921 (via #4837 — record/mutation.ts:735tagNameLower), #1771 (via #4861 — rrweb-snapshot/src/rebuild.ts:444, plus a divergence upstream lacks, see below), #1904 (via #4862 — plugins/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).
* 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.
Track upstream rrweb changes to pull in
We vendor a fork of
rrweb-io/rrwebunderpackages/rrweb/*(moved into this monorepo from the retiredPostHog/posthog-rrwebin #3510). This issue tracks upstream changes worth pulling into our fork, and replaces the old split tracking inPostHog/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.5contains #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 other2.1.5package releases are version/dependency-only for this assessment. Adoption in flight: #5010. Upstream released2.1.2,2.1.3and2.1.4on the same day. Release by release since our fork point:2.0.0mostly re-published pre-alpha.18PRs we already have;2.0.1added one fix (#1854);2.1.0added #1864 + #1865;2.1.1has no code changes (version bumps only);2.1.2added #1712 + #1769;2.1.3added #1921 + #1771;2.1.4has no code changes (version bumps only, plus the #1904 console-plugin fix in that package).2.1.3is 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
Merged upstream since our fork — clean cherry-picks (not yet in our fork)
2.1.2. Upstream deletes thecase EventType.Customskip 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 therrweb/fullscreencustom 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).#fragmentwhen converting absolute->relative CSS urls (e.g.url(#filter)); real correctness gap in ourabsolutifyURLs.ownerDocumentso shadow-DOM detection works under wujie/micro-frontends that patchownerDocument.getCurrentTime()stuck in liveMode after the last event; ourtimer.tshas diverged (rafCheck, guardedtimeOffset) so this needs a careful manual port.Open PRs — Should adopt (High)
<tr>); 11 lines, we provably lack therootNextslogic.pause is not a functionthrow when a site (Squarespace) dispatches media events on non-media elements; cheap guard + try/catch.maskAllInputsdoesn't masktype=hiddeninputs — can leak tokens/PII. (Re-spin of #1610.)Proxyfrom the shared untainted iframe and caches it, so sites whose libraries overwrite globalProxy(old jsPDF) do not break the stylesheet observers. Our fork calls barenew Proxy(...)in 10 places (9 inrrweb/src/record/observer.ts, 1 inrrweb/src/utils.ts) and has nogetUntaintedProxy— so every one of those sites is exposed today. Maintainer-authored, carries #1812 forward, and adds tests on Chromium + WebKit. Read thepackages/utilspart against our#1854divergence before porting — see the note below.*createImageBitmapincallbackWrapperso bitmap errors route through the configured errorHandler instead of bubbling. (Overlaps our #3527.)*<body>never load on replay; rewrites blank-frame detection.*styleId; adds astyleMapbackup.*blockElementFncallback for fine-grained / whitelist blocking policies.*rule?.styleSheet?.hrefso a missing styleSheet no longer nullifies_cssText, leaving<style>empty on replay.*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.*on()so Zone.js-style monkey-patching doesn't break recording.Open PRs — Should adopt (Medium)
maskAllInputs— real PII leak today. (CHANGES_REQUESTED upstream.)<textarea>content; fixesmaskInputFnmissing from initial snapshot.*maskAttributesFnto mask/unmask arbitrary attributes. Framed as an RFC — API may still shift.hookSetter— a real crash; 25 lines, self-contained.insertRule()lost when a<style>is moved during virtual-dom diff (we have the consumer side but not the population).<link rel=stylesheet>attribute-mutation handling + href updates. Hot area for us.grid-template-areas; rebuild cssText fromrule.style.prop: ;declarations that causeCssSyntaxError: Unclosed stringon rebuild. Sits next to ourfixSafariColons.contentWindow). APPROVED; supersedes #1413.toString()so frameworks (Stencil) don't detect a polyfill and break recording.diffChildrenearly-exit that drops sibling elements/styles on a node-type mismatch — real replay corruption.createCDATASectionthrows on HTML docs; switch to a text node. (Different approach from our old empty-CDATA guard — see contribute-back.)image.currentSrc?.startsWith— currentSrc can be undefined and throw during rebuild.recordCanvas:trueso an unrecorded<canvas>doesn't collapse layout.#documentto a non-iframe parent (prevents whole-replay-blank crash).'*'for cross-origin-iframe recording.emitFromIframeso a middle iframe can record directly (nested-iframe case).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)
window.top; superseded conceptually by #1023url(...)in compact inline-style diff path (CHANGES_REQUESTED)Window(record from any iframe)safeCrossOriginguard for cross-origin access errorsprocessMutationsfix (pairs with #1302/#1320)var()shorthand serialization — this is the acceptance test for our #3542 contribute-backchrome-extension://(hyphen-scheme) URLs resolve:hoverby mutating live stylesheets (no postcss); author lists open TODOsisBlockedalongsiderrweb.recordmergeEventsutil (+602 lines, no review):hoverstylesheets (only if we ship that plugin){mode:'closed'}shadow DOM — author flags it as a privacy footgunallowedIframeOriginsallowlist instead ofpostMessage('*'):active,:focus, ...) on rebuild; replay-side onlyNew upstream PRs still open
Triaged against our fork on 2026-09-08. Items that merged into
2.1.3/2.1.4have moved up to the cherry-pick table.packages/utils/src/index.ts, where we already diverge (#1854 / #4128).attributesarray +attributeMapWeakMappair (from #1343) with a singleMap. Our fork has exactly that shape:texts: textCursor[]pushed per mutation (record/mutation.ts:170,:716) and theattributes/attributeMappair (: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.DOMExceptionguard around remove-mutations to rrdom (whose exceptions don't inherit fromDOMException) and toappendChild. Ourreplay/index.ts:1738still has the narrowinstanceof DOMExceptioncheck, so degenerate replay state can still abort a whole mutation.placeholderon<input>/<textarea>throughmaskInputValue()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.srcdocwhen rebuilding an iframe — the browser's own async parse ofsrcdocraces rrweb's reconstruction of the same document.rrdom/src/diff.ts:360already skipssrcdoc;rrweb-snapshot/src/rebuild.tsdoes not, so the two paths disagree.drawImagearguments across realms. Same gap as #1428 from the canvas side.hackCssrewriting of<style>text mutations configurable. Low urgency, replay-side.applyCssSplits/splitCssTextrule-boundary splitting — our fork never vendoredsplitCssText(verified absent).Merged upstream on 2026-09-08 and NOT relevant to us: #1903 (
repository.directorymetadata), #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:
utils/src/index.ts:113–:146, from fix(rrweb): keep untainted-prototype iframe attached on Safari (adopt upstream #1854) #4128) retains the iframe for every key inBasePrototypeCache—Node,ShadowRoot,MutationObserver,Element— for the lifetime of the page, with no cleanup hook.untaintedBaseIframeCleanup[key]remover, which we deliberately left out.MutationObserveronly, because that is the one case that needs a liveScriptExecutionContext(webkit.org/b/179224). Other constructors do not need a live context after retrieval.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:298—this.warn(e); merged upstream 2026-09-04, shipped in2.1.2), #1921 (via #4837 —record/mutation.ts:735tagNameLower), #1771 (via #4861 —rrweb-snapshot/src/rebuild.ts:444, plus a divergence upstream lacks, see below), #1904 (via #4862 —plugins/rrweb-plugin-console-record/src/index.ts:198and:240).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
splitCssTextfeature, 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
PostHogProviderPropstype #1829, [BUG] persistence cookie cause request 400 #1833, chore: skip if Array.from is missing #1475 partial) — not used.@rrweb/browser-client(feat: Screen dimensions and UA as person property #1840, feat: lazy load sampling code #1861),@rrweb/cutter(chore: upgrade rrweb to alpha-13 #1149) — packages we don't vendor.sanitize_properties#1632, fix(flags): Add a shorter configurable timeout for flag requests #1046, chore: missing changelog entry #1290, fix: no keep alive for large payloads #1416, chore(deps): Bump posthog-js from 0.0.0 to 1.63.0 in /react #661, ...) — dropped from our fork.Summary
featureFlags.overridein favor offeatureFlags.overrideFeatureFlags, a new function that supports overriding flags and flag payloads #1697 are now vendored. refactor: Remove/deprecate/rename old options #1694, fix(segment): Posthog can identify after segment identifies a user, not just during bootstrap. #1373 and feat: use gzip compression for decide #1356 were explicitly declined — see the batch-1 comment.2.1.xreleases. Plus Typescript build errors from @rrweb and rrweb-snapshot packages using posthog-js V 1.224.0 #1769, which upstream merged into2.1.2but which our fork already solves more narrowly — a reconcile decision, not a cherry-pick.Proxyrecovery, replaces chore: add new question index to id map #1812 as the batch-2 opener, and settles our revert: feat: always include stack trace parsing #1854 divergence), Downside / upside of importing posthog outside of react functions while using the React SDK #1933 (text-mutation collapsing — the batch-1 recording-CPU theme, from a maintainer), fix: Fix response based branching for open choice questions #1907 (replay mutation abort on degenerate state). All three are still open upstream, so these are adaptations rather than cherry-picks.2.1.xrelease batch is closed (2026-09-10): chore: survey 10kb win #1921 → fix(replay): ignore autoplay mutations regardless of tag name case #4837, feat: capture console errors #1771 → fix(replay): disable autofill on inputs rebuilt for replay #4861, B.extendPostHogWithExceptionAutocapture is not a function #1904 → fix(replay): bind console log wrapper to the logger it patched #4862, all merged. Two were live bugs in our fork rather than missing features. fix(replay): disable autofill on inputs rebuilt for replay #4861 landed one divergence upstream does not have (see Track posthog-js rrweb divergences to contribute back upstream #3766).*PostHog-authored upstream PRs are also tracked in #3766 (contribute back).Generated from a comprehensive scan of upstream
rrweb-io/rrwebon 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 for2.1.2and then for the2.1.3/2.1.4releases; adoption state re-verified againstmainon 2026-09-10. The open-PR list will drift, so re-verify status before acting on an individual row.