Skip to content

fix: lazy-load @reown/appkit to reduce initial bundle size#492

Closed
jfstn wants to merge 1 commit intostx-labs:mainfrom
jfstn:fix/lazy-load-reown-appkit
Closed

fix: lazy-load @reown/appkit to reduce initial bundle size#492
jfstn wants to merge 1 commit intostx-labs:mainfrom
jfstn:fix/lazy-load-reown-appkit

Conversation

@jfstn
Copy link
Copy Markdown
Contributor

@jfstn jfstn commented Feb 13, 2026

Description

As a Stacks Connect consumer, @reown/appkit and its dependency tree (~1.7MB including Lit UI components) are eagerly loaded via top-level imports even if WalletConnect is never used. This causes unnecessary bundle bloat and Lit SSR warnings ("Multiple versions of Lit loaded", "Lit is in dev mode") in frameworks like Next.js.

Motivation for change

Three source files had top-level import statements from @reown/appkit-universal-connector and @reown/appkit/networks. Consumer bundlers (webpack, Vite) eagerly resolve these, pulling in the full @reown/appkit tree — including Lit UI packages (appkit-ui, appkit-scaffold-ui, appkit-pay) that @stacks/connect never uses directly.

What was changed

  1. request.tsimportimport type for UniversalConnectorConfig (only used as a type)
  2. walletconnect/config.ts — replaced runtime @reown/appkit/networks import with inlined CAIP network objects; converted remaining imports to import type
  3. walletconnect/index.ts — converted UniversalConnector to dynamic await import() inside the already-async initializeWalletConnectProvider
  4. tsup.config.ts — added @reown/appkit-universal-connector and @reown/appkit to external array

How does this impact application developers

  • No API changes — WalletConnect.Chains, .Networks, .Default, .initializeProvider all remain exported and unchanged
  • @reown/appkit is now loaded on-demand only when WalletConnect is actually used
  • Eliminates Lit SSR warnings in Next.js and similar frameworks

Before / After

ESM bundle (dist/index.mjs):

Top-level @reown imports Dynamic import()
Before from'@reown/appkit-universal-connector', from'@reown/appkit/networks'
After 0 import('@reown/appkit-universal-connector')

Tested with Next.js 15 consumer app:

@reown/appkit loaded at page load Lit warnings on page load WalletConnect works
Before ~1.7MB (eagerly, even if WalletConnect is never used) "Lit is in dev mode" (server) + 1 browser warning Yes
After 0 (deferred until WalletConnect is clicked) 0 warnings Yes

Type of Change

  • New feature
  • Bug fix
  • API reference/documentation update
  • Other

Does this introduce a breaking change?

No. All public APIs remain unchanged. The only difference is that @reown/appkit-universal-connector is loaded lazily instead of eagerly.

Are documentation updates required?

  • Link to documentation updates:

No documentation updates required.

Testing information

  1. npm run build — succeeds, ESM output has 0 top-level @reown imports
  2. npm run test — all existing tests pass
  3. npm run typecheck — passes
  4. Manually tested with a Next.js 15 app using @stacks/connect — Lit warnings gone, WalletConnect QR modal works on-demand

Checklist

  • Code is commented where needed
  • Unit test coverage for new or modified code paths
  • Changelog is updated
  • Tag @janniks for review

@stacks/connect has two top-level imports from @reown/appkit-universal-connector
and @reown/appkit/networks. These cause consumer bundlers (Next.js, Webpack,
Vite) to eagerly resolve the entire @reown/appkit dependency tree — including
Lit UI components (@reown/appkit-ui, @reown/appkit-scaffold-ui, @reown/appkit-pay,
@reown/appkit-siwx) that @stacks/connect never renders — into the initial
JavaScript bundle. When bundled and minified, this adds ~1.7MB of JavaScript
that must be downloaded, parsed, and executed on every page load, even if
WalletConnect is never used. It also causes "Lit is in dev mode" warnings
during SSR.

Changes:
- request.ts: import → import type for UniversalConnectorConfig
  (type-only usage, erased at compile time)
- walletconnect/config.ts: inline Bitcoin CAIP network objects,
  removing import { bitcoin } from @reown/appkit/networks
- walletconnect/index.ts: dynamic import() for UniversalConnector
  inside the already-async initializeWalletConnectProvider
- tsup.config.ts: explicitly externalize @reown packages

The @reown/appkit code is now only loaded when a user actually clicks
WalletConnect — not on every page load.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copy link
Copy Markdown
Collaborator

@jannik-stacks jannik-stacks left a comment

Choose a reason for hiding this comment

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

Great work, thank you very much for this!

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