Skip to content

fix: NFT tab scroll position#40643

Merged
n3ps merged 4 commits intomainfrom
n3ps/fix-nft-tab
Mar 6, 2026
Merged

fix: NFT tab scroll position#40643
n3ps merged 4 commits intomainfrom
n3ps/fix-nft-tab

Conversation

@n3ps
Copy link
Copy Markdown
Contributor

@n3ps n3ps commented Mar 5, 2026

Description

Switching from Tokens to NFT tab caused the scroll position to jump. Two causes:

  1. useNftsCollections and useNfts used useEffect + useState to compute derived state from Redux (an anti-pattern). This delayed NFT data by one render tick, causing NftsTab to render a loading spinner with zero height and causing the scroll container's scrollTop to 0. Refactored both hooks to use useMemo so data is computed synchronously on first render. Also removed dead code several imports in the useEffect dependency arrays were never actually used.

  2. The virtualizer default scrollToFn applies measurement adjustments. When estimated size differ from measured size, react-virtual corrects the scroll position. Because the NFT tab has dynamic rows this adjustment can be cause a wrong scroll position. Added a noAdjustmentsScrollToFn option to VirtualizedList.

Open in GitHub Codespaces

Changelog

CHANGELOG entry: fix: NFT tab scroll position

Related issues

Fixes:

Manual testing steps

  1. Use an account with MANY NFTs
  2. Switch to the NFT tab

Screenshots/Recordings

Before

NFT.scroll.-.BEFORE.mov

After

Pre-merge author checklist

Pre-merge reviewer checklist

  • I've manually tested the PR (e.g. pull and build branch, run the app, test code being changed).
  • I confirm that this PR addresses all acceptance criteria described in the ticket it closes and includes the necessary testing evidence such as recordings and or screenshots.

Note

Medium Risk
Touches shared VirtualizedList virtualization behavior and refactors NFT data hooks to compute synchronously; mistakes could affect scroll position or rendering in other virtualized lists.

Overview
Fixes NFT tab scroll jumps when switching tabs by removing one-render-tick delays in NFT-derived data and by disabling react-virtual scroll offset “adjustments”.

useNfts and useNftsCollections are refactored from useEffect+useState to synchronous useMemo derivations (and NftsTab removes the interim loading spinner/trace dependency). VirtualizedList now accepts an optional scrollToFn (with new noAdjustmentsScroll) and forces a measure() when the scroll container is available; NftGrid adopts these options, tweaks estimated row sizing, and adds a stable row keyExtractor.

Written by Cursor Bugbot for commit 0567653. This will update automatically on new commits. Configure here.

@n3ps n3ps added the team-core-extension-ux Core Extension UX team label Mar 5, 2026
@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Mar 5, 2026

CLA Signature Action: All authors have signed the CLA. You may need to manually re-run the blocking PR check if it doesn't pass in a few minutes.

@metamaskbotv2
Copy link
Copy Markdown
Contributor

metamaskbotv2 bot commented Mar 5, 2026

✨ Files requiring CODEOWNER review ✨

💎 @MetaMask/metamask-assets (2 files, +20 -56)
  • 📁 ui/
    • 📁 components/
      • 📁 app/
        • 📁 assets/
          • 📁 nfts/
            • 📁 nft-grid/
              • 📄 nft-grid.tsx +17 -31
            • 📁 nfts-tab/
              • 📄 nfts-tab.tsx +3 -25

@github-actions github-actions bot added the size-M label Mar 5, 2026

// Container width threshold for switching between 3 and 4 columns
const CONTAINER_WIDTH_THRESHOLD = 640;
const ESTIMATED_ROW_SIZE = 172;
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

An approximation is recommended

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

how did we set this here ?

Copy link
Copy Markdown
Contributor Author

@n3ps n3ps Mar 6, 2026

Choose a reason for hiding this comment

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

Previously inline value below

This is an initial estimate that a virtualizer recommends, it still adjusts after render

@n3ps n3ps requested a review from a team March 5, 2026 20:39
export function useNfts({
overridePopularNetworkFilter = false,
}: {
overridePopularNetworkFilter?: boolean;
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

dead code

@n3ps n3ps marked this pull request as ready for review March 5, 2026 21:16
@n3ps n3ps requested a review from a team as a code owner March 5, 2026 21:16
@n3ps n3ps enabled auto-merge March 5, 2026 21:45
@metamaskbotv2
Copy link
Copy Markdown
Contributor

metamaskbotv2 bot commented Mar 5, 2026

Builds ready [06911ff]
⚡ Performance Benchmarks
👆 Interaction Benchmarks
BenchmarkMetricMean (ms)Min (ms)Max (ms)Std Dev (ms)P75 (ms)P95 (ms)
Load New Accountload_new_account29226534429303344
total29226534429303344
Confirm Txconfirm_tx608260816083160836083
total608260816083160836083
Bridge User Actionsbridge_load_page23921326621252266
bridge_load_asset_picker18514922331219223
bridge_search_token7087047113709711
total1117110011471811101147
🔌 Startup Benchmarks
BenchmarkMetricMean (ms)Min (ms)Max (ms)Std Dev (ms)P75 (ms)P95 (ms)
Standard HomeuiStartup14911231198211815431689
load12551036169610813031443
domContentLoaded12481032168910412971432
domInteractive3118119212791
firstPaint199741448222233340
backgroundConnect23020729516236264
firstReactRender20124252231
initialActions208125
loadScripts1036818147710410871208
setupStore1475071628
numNetworkReqs312291192284
Power User HomeuiStartup297716448733157635146116
load12811080171414213221584
domContentLoaded12641068170313913001567
domInteractive3522138193757
firstPaint210861273143279352
backgroundConnect10272725021111911043186
firstReactRender26165472939
initialActions104112
loadScripts1037864144313310611348
setupStore1664981735
numNetworkReqs1013422843130180
🧭 User Journey Benchmarks
BenchmarkMetricMean (ms)Min (ms)Max (ms)Std Dev (ms)P75 (ms)P95 (ms)
Onboarding Import WalletimportWalletToSocialScreen2182162212219221
srpButtonToSrpForm93929419494
confirmSrpToPwForm21212102121
pwFormToMetricsScreen15151501515
metricsToWalletReadyScreen15151611516
doneButtonToHomeScreen67658874968744749
openAccountMenuToAccountListLoaded2914290029361629312936
total3955384740166840154016
Onboarding New WalletcreateWalletToSocialScreen2192192190219219
srpButtonToPwForm1081051102110110
createPwToRecoveryScreen888088
skipBackupToMetricsScreen35343613636
agreeButtonToOnboardingSuccess16161601616
doneButtonToAssetList55048363159595631
total9378661019609851019
Asset DetailsassetClickToPriceChart1201171243120124
total1201171243120124
Solana Asset DetailsassetClickToPriceChart1068413018117130
total1068413018117130
Import Srp HomeloginToHomeScreen1972194020233219742023
openAccountMenuAfterLogin41364744147
homeAfterImportWithNewWallet27892596300715929063007
total47434574491715048654917
Send TransactionsopenSendPageFromHome33224063740
selectTokenToSendFormLoaded321960164060
reviewTransactionToConfirmationPage1200885176236715161762
total1265927184338516131843
SwapopenSwapPageFromHome41414204242
fetchAndDisplaySwapQuotes269926962702327022702
total273827292743627432743
🌐 Dapp Page Load Benchmarks

Current Commit: 06911ff | Date: 3/5/2026

📄 Localhost MetaMask Test Dapp

Samples: 100

Summary

  • pageLoadTime-> current mean value: 1.05s (±40ms) 🟡 | historical mean value: 1.04s ⬆️ (historical data)
  • domContentLoaded-> current mean value: 724ms (±38ms) 🟢 | historical mean value: 732ms ⬇️ (historical data)
  • firstContentfulPaint-> current mean value: 80ms (±9ms) 🟢 | historical mean value: 83ms ⬇️ (historical data)

📈 Detailed Results

Metric Mean Std Dev Min Max P95 P99
pageLoadTime 1.05s 40ms 1.02s 1.34s 1.08s 1.34s
domContentLoaded 724ms 38ms 697ms 1.00s 749ms 1.00s
firstPaint 80ms 9ms 64ms 152ms 92ms 152ms
firstContentfulPaint 80ms 9ms 64ms 152ms 92ms 152ms
largestContentfulPaint 0ms 0ms 0ms 0ms 0ms 0ms
Bundle size diffs [🚨 Warning! Bundle size has increased!]
  • background: 58 Bytes (0%)
  • ui: 25 KiB (0.3%)
  • common: 913 Bytes (0.01%)

@n3ps n3ps changed the title fix: NFT tab loading fix: NFT tab scroll position Mar 5, 2026
@sonarqubecloud
Copy link
Copy Markdown

sonarqubecloud bot commented Mar 5, 2026

@metamaskbotv2
Copy link
Copy Markdown
Contributor

metamaskbotv2 bot commented Mar 5, 2026

Builds ready [0567653]
⚡ Performance Benchmarks
👆 Interaction Benchmarks
BenchmarkMetricMean (ms)Min (ms)Max (ms)Std Dev (ms)P75 (ms)P95 (ms)
Load New Accountload_new_account2732662805277280
total2732662805277280
Confirm Txconfirm_tx6072604760971960796097
total6072604760971960796097
Bridge User Actionsbridge_load_page28323034036299340
bridge_load_asset_picker20810933778232337
bridge_search_token72970477530754775
total12201084134411413261344
🔌 Startup Benchmarks
BenchmarkMetricMean (ms)Min (ms)Max (ms)Std Dev (ms)P75 (ms)P95 (ms)
Standard HomeuiStartup14421215178412014761679
load12061028148010612471423
domContentLoaded11981013146910512401413
domInteractive3018143212778
firstPaint169731179131214353
backgroundConnect21920025813225245
firstReactRender20144862131
initialActions105123
loadScripts996811126610310291207
setupStore1364061625
numNetworkReqs312290192286
Power User HomeuiStartup2187169212051104121082713
load11671024182515711541631
domContentLoaded11531016181615711441610
domInteractive3418117193486
firstPaint1687950986230322
backgroundConnect4262689260896334472
firstReactRender24165672638
initialActions106112
loadScripts93381215721539231369
setupStore1574061628
numNetworkReqs1044328959130245
🧭 User Journey Benchmarks
BenchmarkMetricMean (ms)Min (ms)Max (ms)Std Dev (ms)P75 (ms)P95 (ms)
Onboarding Import WalletimportWalletToSocialScreen2192172201220220
srpButtonToSrpForm97949819898
confirmSrpToPwForm22212512225
pwFormToMetricsScreen16151811618
metricsToWalletReadyScreen17161811718
doneButtonToHomeScreen67159575873729758
openAccountMenuToAccountListLoaded3037292131269031233126
total40603940425611240924256
Onboarding New WalletcreateWalletToSocialScreen2212172232222223
srpButtonToPwForm1121051247116124
createPwToRecoveryScreen888088
skipBackupToMetricsScreen35343613636
agreeButtonToOnboardingSuccess16151601616
doneButtonToAssetList55647460150597601
total94986399048984990
Asset DetailsassetClickToPriceChart1151031248118124
total1151031248118124
Solana Asset DetailsassetClickToPriceChart75747617576
total75747617576
Import Srp HomeloginToHomeScreen21141939235916222522359
openAccountMenuAfterLogin603996206796
homeAfterImportWithNewWallet24742363262912226192629
total4704461648167447194816
Send TransactionsopenSendPageFromHome27193663036
selectTokenToSendFormLoaded381964164864
reviewTransactionToConfirmationPage8588478709864870
total92488995825931958
SwapopenSwapPageFromHome482485246585
fetchAndDisplaySwapQuotes269126872697326912697
total2739271227722227562772
🌐 Dapp Page Load Benchmarks

Current Commit: 0567653 | Date: 3/5/2026

📄 Localhost MetaMask Test Dapp

Samples: 100

Summary

  • pageLoadTime-> current mean value: 1.06s (±46ms) 🟡 | historical mean value: 1.04s ⬆️ (historical data)
  • domContentLoaded-> current mean value: 746ms (±64ms) 🟢 | historical mean value: 732ms ⬆️ (historical data)
  • firstContentfulPaint-> current mean value: 94ms (±131ms) 🟢 | historical mean value: 83ms ⬆️ (historical data)

📈 Detailed Results

Metric Mean Std Dev Min Max P95 P99
pageLoadTime 1.06s 46ms 1.02s 1.42s 1.10s 1.42s
domContentLoaded 746ms 64ms 715ms 1.33s 775ms 1.33s
firstPaint 94ms 131ms 60ms 1.40s 96ms 1.40s
firstContentfulPaint 94ms 131ms 60ms 1.40s 96ms 1.40s
largestContentfulPaint 0ms 0ms 0ms 0ms 0ms 0ms
Bundle size diffs [🚨 Warning! Bundle size has increased!]
  • background: -829 Bytes (-0.02%)
  • ui: 25.61 KiB (0.31%)
  • common: 841 Bytes (0.01%)

@n3ps n3ps added this pull request to the merge queue Mar 6, 2026
Merged via the queue into main with commit c03d180 Mar 6, 2026
346 of 348 checks passed
@n3ps n3ps deleted the n3ps/fix-nft-tab branch March 6, 2026 17:39
@github-actions github-actions bot locked and limited conversation to collaborators Mar 6, 2026
@metamaskbot metamaskbot added the release-13.23.0 Issue or pull request that will be included in release 13.23.0 label Mar 6, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

release-13.23.0 Issue or pull request that will be included in release 13.23.0 size-M team-core-extension-ux Core Extension UX team

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants