perf(wallet): load the wallet modal on demand (-55% JS on /onboard) - #174
Merged
Conversation
`createAppKit()` ran at module scope in components/providers.tsx, and app/layout.tsx renders that provider for every route. So anyone reading the landing page - or an operator on /onboard who has not gone near a wallet - downloaded, parsed and executed the entire wallet stack first: AppKit's UI, WalletConnect, and the Coinbase SDK that `enableCoinbase: false` disables at runtime but cannot remove from the bundle. Measured against production, that was a single 1,395 KB chunk - 53% of everything /onboard shipped. On a WebKitGTK desktop WebView at 3840x2160 it is several megabytes of JavaScript to compile before the page becomes interactive, which is what "the app feels heavy" turned out to mean. It was not the machine: hardware GL was fine and idle CPU was 0%. The modal now builds on the first openWallet() call (lib/appkit.ts), cached in a module-level promise so concurrent clicks share one download and createAppKit runs exactly once - calling it twice registers a second modal and the account state desynchronises. Connect CTAs prefetch on hover, so the chunk is usually warm before the click lands. Reading connection state no longer goes through AppKit. ConnectButton uses wagmi's useAccount/useChainId, which are already in the eager bundle because WagmiProvider still wraps the tree - so the button renders, and still shows a reconnected address after a reload, with AppKit absent. route before after /onboard 708 kB 316 kB -55% /build/bridge 752 kB 361 kB -52% /dashboard 313 kB 314 kB unchanged Verified: tsc clean, 673 tests pass, eslint clean, next build succeeds. NOT included: stubbing out @coinbase/wallet-sdk. It is reachable only through wagmi's dynamic connector loading, so a resolve.alias never intercepts it - the stub reached no chunk and total client JS did not move. It would have added risk to the connect path for no measurable gain.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why the desktop app felt heavy
Not the machine. On the reporting box hardware GL was healthy (
direct rendering: Yes, OpenGL 4.6), idle CPU was 0%, and network RTT was 1.7 ms. The weight was JavaScript.createAppKit()ran at module scope incomponents/providers.tsx, whichapp/layout.tsxrenders for every route. So the landing page — and/onboard, before anyone touches a wallet — downloaded and executed the whole wallet stack up front. Measured against production: a single 1,395 KB chunk, 53% of everything/onboardshipped.What changed
The modal is now built on the first
openWallet()call (lib/appkit.ts), cached in a module-level promise so concurrent clicks share one download andcreateAppKitruns exactly once — calling it twice registers a second modal and account state desynchronises. Connect CTAs prefetch on hover.Crucially, reading connection state no longer touches AppKit:
ConnectButtonuses wagmi'suseAccount/useChainId, already in the eager bundle becauseWagmiProviderstill wraps the tree. The button renders — and still shows a reconnected address after reload — with AppKit absent./onboard/build/bridge/dashboardDeliberately not included
Stubbing out
@coinbase/wallet-sdk. It's reachable only via wagmi's dynamic connector loading, so aresolve.aliasnever intercepts it — I tried it, the stub reached no chunk and total client JS didn't move (5.64 MB either way). Risk to the connect path for no gain.Verification
tscclean · 673 tests · eslint clean ·next buildsucceeds.Please click through a real wallet connect before merging. This restructures the connect path, and that's the one place a green build proves least — automated gates can't see whether the modal actually opens and signs.