Skip to content

Resolve Dialog root containers in linear time - #3907

Open
MomchilGorchev wants to merge 2 commits into
tailwindlabs:mainfrom
MomchilGorchev:fix-root-containers-quadratic
Open

MomchilGorchev wants to merge 2 commits into
tailwindlabs:mainfrom
MomchilGorchev:fix-root-containers-quadratic

Conversation

@MomchilGorchev

Copy link
Copy Markdown

useRootContainers collects the elements that count as "inside" a Dialog. For each html > *, body > * element, it checks whether that element contains any container resolved so far, including the root elements it has already pushed. That list grows by one per root element, so resolving the containers is quadratic in the number of <body> children.

This runs on every outside click check while a Dialog is open. On most pages <body> has a few dozen children, so it doesn't matter. We have production sessions where something outside the app, most likely a browser extension, leaves 7,000 to 18,000 elements in <body>. In those sessions every click inside an open Dialog blocks the main thread for 1 to 5 seconds, all of it inside the use-outside-click document listener.

Root elements are siblings, so one can never contain another. This PR compares each root element only with the default containers and portals, which is what the existing comment on that line describes. The resolved containers are the same, and the loop is linear.

Time per click inside an open Dialog (longest animation frame, Chromium, 2.2.2 with this change applied as a patch):

<body> children Before After
5,000 240 ms 21 ms
10,000 960 ms 30 ms
17,479 3,110 ms 24 to 44 ms

Changes:

  • useRootContainers in @headlessui/react and @headlessui/vue.
  • A Dialog test in each package that counts Node.prototype.contains calls for one click with 1,000 extra <body> children. It fails on main (502,503 calls against a limit of 50,000) and passes with this change.
  • Changelog entries for both packages.

@vercel

vercel Bot commented Oct 23, 2026

Copy link
Copy Markdown

@MomchilGorchev is attempting to deploy a commit to the Tailwind Labs Team on Vercel.

A member of the Team first needs to authorize it.

This branch has not been deployed

No deployments
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.

2 participants