Skip to content

perf(useSlots): optimize slot matching with short-circuit and reduced allocations#7555

Merged
hectahertz merged 11 commits intomainfrom
hectahertz/perf-use-slots-short-circuit
Feb 20, 2026
Merged

perf(useSlots): optimize slot matching with short-circuit and reduced allocations#7555
hectahertz merged 11 commits intomainfrom
hectahertz/perf-use-slots-short-circuit

Conversation

@hectahertz
Copy link
Contributor

@hectahertz hectahertz commented Feb 15, 2026

Closes #

Optimizes the useSlots hook used by 8+ components (ActionList.Item, ActionList, FormControl, TreeView, NavList, PageLayout, Dialog, CheckboxOrRadioGroup). The hook iterates children on every render to extract slot components.

Changes:

  • Production short-circuit: once all slots are filled, remaining children go directly to rest with zero type checking (single integer comparison). Duplicate detection only runs in __DEV__ and is stripped in production builds.
  • for loop instead of findIndex: eliminates closure allocation per child per render
  • for loop init instead of mapValues+Object.keys().reduce(): replaces a higher-order reduce with a simple loop to initialize slot keys

Metrics

Microbenchmark (Chrome, 100K iterations, JIT-warmed):

Operation Old New Speedup
Slot init (mapValues+reduce vs for loop) 0.05 us 0.04 us 20%
Child matching (findIndex+closure vs for loop) 0.07 us 0.05 us 31%
Full useSlots call 0.21 us 0.14 us 33%

Stress test (ActionList, 100 items x 100 re-renders):

Metric main Optimized Change
DevTools trace duration (100 re-renders) 19,014 ms 17,891 ms -6%
Story-reported median 7.23 ms 6.70 ms -7%
Story-reported average 5.42 ms 4.37 ms -19%

Real-world impact is larger than this test shows: 8+ components call useSlots, production builds strip the __DEV__ duplicate-detection loop.

Changelog

New

N/A

Changed

  • useSlots: optimized child iteration with production short-circuit and reduced allocations

Removed

N/A

Rollout strategy

  • Patch release
  • Minor release
  • Major release; if selected, include a written rollout or migration plan
  • None; if selected, include a brief description as to why

Testing & Reviewing

  1. All 13 original useSlots unit tests pass unmodified (behavior-preserving)
  2. 4 new tests added covering: short-circuit fast path, non-element children handling, slot order independence, single-slot config edge case
  3. All 303 dependent component tests pass (ActionList, ActionMenu, NavList, FormControl, TreeView, PageLayout)

Merge checklist

  • Added/updated tests
  • Added/updated documentation
  • Added/updated previews (Storybook)
  • Changes are SSR compatible
  • Tested in Chrome
  • Tested in Firefox
  • Tested in Safari
  • Tested in Edge
  • (GitHub staff only) Integration tests pass at github/github

@changeset-bot
Copy link

changeset-bot bot commented Feb 15, 2026

🦋 Changeset detected

Latest commit: ef3c4aa

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@primer/react Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@github-actions
Copy link
Contributor

👋 Hi, this pull request contains changes to the source code that github/github-ui depends on. If you are GitHub staff, test these changes with github/github-ui using the integration workflow. Or, apply the integration-tests: skipped manually label to skip these checks.

@github-actions github-actions bot added the integration-tests: recommended This change needs to be tested for breaking changes. See https://arc.net/l/quote/tdmpakpm label Feb 15, 2026
@github-actions github-actions bot temporarily deployed to storybook-preview-7555 February 15, 2026 10:36 Inactive
@hectahertz hectahertz changed the title perf(useSlots): short-circuit iteration and skip filled slots [pending metrics] perf(useSlots): short-circuit iteration and skip filled slots [pending metrics and tests] Feb 15, 2026
@hectahertz hectahertz force-pushed the hectahertz/perf-use-slots-short-circuit branch 2 times, most recently from 91c8339 to 4a250c7 Compare February 15, 2026 10:49
@github-actions github-actions bot requested a deployment to storybook-preview-7555 February 15, 2026 10:51 Abandoned
@hectahertz hectahertz force-pushed the hectahertz/perf-use-slots-short-circuit branch from 4a250c7 to c924742 Compare February 15, 2026 10:52
@hectahertz hectahertz force-pushed the hectahertz/perf-use-slots-short-circuit branch from c924742 to a1d1272 Compare February 15, 2026 10:53
@hectahertz hectahertz changed the title perf(useSlots): short-circuit iteration and skip filled slots [pending metrics and tests] perf(useSlots): short-circuit iteration and skip filled slots [pending metrics] Feb 15, 2026
@hectahertz hectahertz changed the title perf(useSlots): short-circuit iteration and skip filled slots [pending metrics] perf(useSlots): optimize slot matching with short-circuit and reduced allocations [pending metrics] Feb 15, 2026
@github-actions github-actions bot requested a deployment to storybook-preview-7555 February 15, 2026 10:56 Abandoned
@github-actions github-actions bot requested a deployment to storybook-preview-7555 February 15, 2026 11:03 Abandoned
@hectahertz hectahertz changed the title perf(useSlots): optimize slot matching with short-circuit and reduced allocations [pending metrics] perf(useSlots): optimize slot matching with short-circuit and reduced allocations Feb 15, 2026
@hectahertz hectahertz changed the title perf(useSlots): optimize slot matching with short-circuit and reduced allocations perf(useSlots): optimize slot matching with short-circuit and reduced allocations [pending metrics] Feb 15, 2026
@github-actions github-actions bot temporarily deployed to storybook-preview-7555 February 15, 2026 11:16 Inactive
@hectahertz hectahertz force-pushed the hectahertz/perf-use-slots-short-circuit branch from fe1f7f0 to 8f048b0 Compare February 15, 2026 11:21
@hectahertz hectahertz changed the title perf(useSlots): optimize slot matching with short-circuit and reduced allocations [pending metrics] perf(useSlots): optimize slot matching with short-circuit and reduced allocations Feb 15, 2026
@github-actions github-actions bot requested a deployment to storybook-preview-7555 February 15, 2026 11:22 Abandoned
@github-actions github-actions bot requested a deployment to storybook-preview-7555 February 15, 2026 11:26 Abandoned
@github-actions github-actions bot temporarily deployed to storybook-preview-7555 February 15, 2026 11:37 Inactive
@hectahertz hectahertz requested a review from a team as a code owner February 18, 2026 18:22
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR optimizes the useSlots hook in @primer/react, which is used widely across components to extract “slot” children while remaining SSR-compatible. The changes focus on reducing per-render allocations and enabling faster matching in common cases.

Changes:

  • Replaces higher-order utilities (mapValues, findIndex) with for loops to reduce allocations and overhead.
  • Adds a “all slots filled” short-circuit path to avoid further matching work.
  • Adds/extends unit tests for additional slot behaviors and introduces a changeset for a patch release.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.

File Description
packages/react/src/hooks/useSlots.ts Refactors slot initialization/matching logic and adds a short-circuit path + helper functions.
packages/react/src/hooks/tests/useSlots.test.tsx Adds tests for short-circuit behavior, non-element children handling, and slot order independence.
.changeset/perf-use-slots-short-circuit.md Declares a patch release entry for the perf change.

Comment on lines +116 to 122
// Short-circuit: all slots filled, no more matching needed
if (slotsFound === totalSlots) {
if (__DEV__ && warnIfDuplicate(child, keys, values, totalSlots)) {
return
}
})

// If the child is not a slot, add it to the `rest` array
if (index === -1) {
rest.push(child)
return
Copy link

Copilot AI Feb 18, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In production builds (DEV is false), the short-circuit path pushes all subsequent elements into rest without checking whether they match a slot. This changes behavior vs the previous implementation: duplicate slot children that appear after all slots are filled will now be treated as rest (and can be rendered), even though the warning message/semantics say duplicates are ignored (“Only the first will be rendered.”). To preserve prior behavior, avoid adding children that match any slot to rest even in the short-circuit path (you can still keep it cheap by first checking child.type against a precomputed set of configured component matchers before running the full matcher/testFn logic).

Copilot uses AI. Check for mistakes.
*/
function warnIfDuplicate(
child: React.ReactElement,
keys: Array<string | number | symbol>,
Copy link

Copilot AI Feb 18, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warnIfDuplicate takes keys: Array<string | number | symbol>, but keys is produced via Object.keys(config) and cast to Array<keyof Config> (effectively strings). Tightening this helper signature to Array<keyof Config> (or string[]) would better reflect reality and avoid type widening that can mask mistakes.

Suggested change
keys: Array<string | number | symbol>,
keys: string[],

Copilot uses AI. Check for mistakes.
@hectahertz hectahertz enabled auto-merge February 18, 2026 19:00
@primer-integration
Copy link

👋 Hi from github/github-ui! Your integration PR is ready: https://github.com/github/github-ui/pull/14138

@primer-integration
Copy link

Integration test results from github/github-ui:

Passed  CI   Passed
Passed  VRT   Passed
Passed  Projects   Passed

All checks passed!

@hectahertz hectahertz added this pull request to the merge queue Feb 20, 2026
Merged via the queue into main with commit 64c2243 Feb 20, 2026
52 checks passed
@hectahertz hectahertz deleted the hectahertz/perf-use-slots-short-circuit branch February 20, 2026 14:36
@primer primer bot mentioned this pull request Feb 20, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

integration-tests: recommended This change needs to be tested for breaking changes. See https://arc.net/l/quote/tdmpakpm

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants