SharePoint / SharePoint/sp-dev-docs

Performance Leak: Unbound addHook registration in _sanitizeLinks causes main-thread blocking and input lag

Open
#10,988 2 comments 0 reactions 1 assignee View on GitHub

@Ashlesha-MSFT is already working on this.

Since Aug 12, 2026.

sharepoint-developer-support type:bug-confirmed
Dominant language
PowerShell
Stars
1.4k
Forks
1.1k
Avg merge
4d 12h
Merged PRs (30d)
12

Description

Target SharePoint environment

SharePoint Online

What SharePoint development model, framework, SDK or API is this about?

💥 SharePoint Framework

Developer environment

Windows

What browser(s) / client(s) have you tested
  • 💥 Internet Explorer
  • 💥 Microsoft Edge
  • 💥 Google Chrome
  • 💥 FireFox
  • 💥 Safari
  • mobile (iOS/iPadOS)
  • mobile (Android)
  • not applicable
  • other (enter in the "Additional environment details" area below)
Additional environment details
  • Microsoft Windows 11

  • Microsoft Edge Version 151.0.4129.79

  • Google Chrome Version 151.0.7922.72

  • Firefox Version 153.0.3

  • Occurs when accessing Viva Home / SharePoint integrated pages via Microsoft Teams on the Web via Microsoft Edge and Google Chrome

  • Firefox does not appear to be impacted

Describe the bug / error

Over extended usage sessions (typically a few hours), accessing SharePoint-integrated pages via Microsoft Teams on the Web results in severe main-thread blocking and keyboard input lag (up to ~1800ms per keystroke/timer execution).

Profiling in Chromium DevTools reveals a long task originating from Timer fired events that eventually call into _sanitizeLinks() inside sp-pages-assembly_en-us.js.

Image

Every time _sanitizeLinks() is called during component lifecycle updates or tab navigation, it executes:

static _sanitizeLinks() {
	ab.addHook("afterSanitizeAttributes", a => {
		let b = document.createElement("a")
		  , c = a.getAttribute("href");
		c && (b.href = c,
		b.protocol && !this.isSafeLinkProtocol(b.protocol) && a.removeAttribute("href"));
		let d = a.getAttribute("action");
		d && (b.href = d,
		b.protocol && !this.isSafeLinkProtocol(b.protocol) && a.removeAttribute("action"));
		let e = a.getAttribute("xlink:href");
		e && (b.href = e,
		b.protocol && !this.isSafeLinkProtocol(b.protocol) && a.removeAttribute("xlink:href"));
	}
	);
}

Because the ab sanitizer instance persists globally across component mounts, calling addHook repeatedly appends identical callbacks without removing old ones. Over time, thousands of hooks accumulate in the handler array. When sanitization triggers, Chromium executes document.createElement("a") up to three times per hook, causing catastrophic main-thread starvation.

Adding the following lines to the function via a local file override in Chrome resolves the issue:

if (this._hasAttachedHook) return;
this._hasAttachedHook = true;
Steps to reproduce
  1. Open Microsoft Teams on the Web using Google Chrome or Microsoft Edge.
  2. Keep the tab open and actively navigate and participate between different group chats over a few hours to allow lifecycle re-renders to accumulate hooks
  3. Record a Performance Trace in Chrome/Edge DevTools while typing in an input field to capture input lag
  4. Inspect the long task in the Performance flame graph under "Timer Fired"
Expected behavior

_sanitizeLinks() should only attach its afterSanitizeAttributes hook once per sanitizer instance, or detach the hook on component unmount, ensuring single-digit millisecond sanitization performance regardless of session duration.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.