User Story
As a first-time visitor to the Vortex landing page
I want the page to load and animate quickly
So that I get a smooth first impression of the product without waiting for blockchain infrastructure I haven't asked for yet
Acceptance Criteria
Additional Context
Size: M
Findings
The landing page currently eagerly loads ~7 MB of blockchain vendor code (wagmi, @walletconnect, @polkadot, stellar-sdk) on every visit, including users who never open the widget. This causes sluggish initial render and janky hero animations.
Investigation revealed five root causes:
-
manualChunks makes vendor-evm a shared runtime host. Splitting vendors by package name forces Rollup to assign any internally-shared utility to the matched vendor chunk. Every marketing page route ends up with a static import to vendor-evm at build time — including components like AnimatedTitle — even though they have no real wagmi dependency in source. This is a Rollup artifact of the chunking strategy, not a source-level problem.
-
manualChunks increases total size vs the original monolith. Before chunking, Rollup deduplicated everything once. Named chunks introduce cross-chunk import overhead and prevent deduplication, making total bytes larger than the original 8 MB bundle.
-
@vortexfi/shared resolves to raw TypeScript source. The browser export condition in packages/shared/package.json points to ./src/index.ts — a full export * barrel that includes services/ (polkadot, stellar, wagmi helpers). Even with sideEffects: false, the barrel is too wide for Rollup to tree-shake effectively when used from the landing page.
-
services/api/index.ts is an unguarded barrel. It re-exports moonbeam.service, polkadot.service, and pendulum.service. Any landing page component that imports from this barrel (e.g. useFeeComparisonData → PriceService) transitively pulls in wagmi and polkadot.
-
The lazy boundary is on the React component, not the module graph. widget.lazy.tsx defers the component render but the heavy vendor transitive deps remain statically reachable from the entry chunk via shared Rollup intermediate chunks.
Hints
FeeComparison on the landing page imports useNetwork (→ contexts/network → wagmi) and useEventsContext (→ contexts/events → same chain). These should be removed — FeeComparison only needs EVM rates and has no real dependency on wallet state.
useFeeComparisonData imports PriceService via the services/api barrel. Changing it to import directly from services/api/price.service severs the polkadot/moonbeam chain for that component.
packages/shared/package.json should have "sideEffects": false to allow Rollup to tree-shake the barrel. Combined with pointing browser to ./dist/browser/index.js (pre-built ESM), this should eliminate the raw source barrel problem.
rollup-plugin-visualizer is already configured in vite.config.ts — use it after each change to verify the import graph.
Desired Approach
- Remove
manualChunks entirely from vite.config.ts. Let Rollup manage chunking automatically once the module graph is clean.
- Audit the static import graph from
main.tsx — trace every path that reaches wagmi, @polkadot, stellar-sdk, @walletconnect, or @reown and cut each one.
- Sever context imports from marketing components — no file outside
widget.lazy.tsx and its descendants should import useNetwork, useEventsContext, or any hook that chains to a blockchain SDK.
- Replace barrel imports with direct imports in any file reachable from the landing page (e.g.
services/api/price.service instead of services/api).
- Fix
@vortexfi/shared browser export — point it to ./dist/browser/index.js and add "sideEffects": false. Rebuild shared before testing the frontend.
- Validate with the bundle analyser — the landing page preload manifest should list zero chunks that transitively import a blockchain vendor.
Affected surfaces (per CONTEXT.md): Initial bundle tier, widget.lazy.tsx boundary, MarketingLayout, FeeComparison section, @vortexfi/shared package exports.
User Story
As a first-time visitor to the Vortex landing page
I want the page to load and animate quickly
So that I get a smooth first impression of the product without waiting for blockchain infrastructure I haven't asked for yet
Acceptance Criteria
main.tsxreacheswagmi,@polkadot,stellar-sdk,@walletconnect, or@reownat build time — verified via bundle analyser (rollup-plugin-visualizeris already configured invite.config.ts)@vortexfi/sharedbrowser export resolves to a pre-built ESM artifact, not raw TypeScript source (./src/index.ts)Additional Context
Size: M
Findings
The landing page currently eagerly loads ~7 MB of blockchain vendor code (
wagmi,@walletconnect,@polkadot,stellar-sdk) on every visit, including users who never open the widget. This causes sluggish initial render and janky hero animations.Investigation revealed five root causes:
manualChunksmakesvendor-evma shared runtime host. Splitting vendors by package name forces Rollup to assign any internally-shared utility to the matched vendor chunk. Every marketing page route ends up with a staticimporttovendor-evmat build time — including components likeAnimatedTitle— even though they have no real wagmi dependency in source. This is a Rollup artifact of the chunking strategy, not a source-level problem.manualChunksincreases total size vs the original monolith. Before chunking, Rollup deduplicated everything once. Named chunks introduce cross-chunk import overhead and prevent deduplication, making total bytes larger than the original 8 MB bundle.@vortexfi/sharedresolves to raw TypeScript source. Thebrowserexport condition inpackages/shared/package.jsonpoints to./src/index.ts— a fullexport *barrel that includesservices/(polkadot, stellar, wagmi helpers). Even withsideEffects: false, the barrel is too wide for Rollup to tree-shake effectively when used from the landing page.services/api/index.tsis an unguarded barrel. It re-exportsmoonbeam.service,polkadot.service, andpendulum.service. Any landing page component that imports from this barrel (e.g.useFeeComparisonData→PriceService) transitively pulls in wagmi and polkadot.The lazy boundary is on the React component, not the module graph.
widget.lazy.tsxdefers the component render but the heavy vendor transitive deps remain statically reachable from the entry chunk via shared Rollup intermediate chunks.Hints
FeeComparisonon the landing page importsuseNetwork(→contexts/network→wagmi) anduseEventsContext(→contexts/events→ same chain). These should be removed —FeeComparisononly needs EVM rates and has no real dependency on wallet state.useFeeComparisonDataimportsPriceServicevia theservices/apibarrel. Changing it to import directly fromservices/api/price.servicesevers the polkadot/moonbeam chain for that component.packages/shared/package.jsonshould have"sideEffects": falseto allow Rollup to tree-shake the barrel. Combined with pointingbrowserto./dist/browser/index.js(pre-built ESM), this should eliminate the raw source barrel problem.rollup-plugin-visualizeris already configured invite.config.ts— use it after each change to verify the import graph.Desired Approach
manualChunksentirely fromvite.config.ts. Let Rollup manage chunking automatically once the module graph is clean.main.tsx— trace every path that reacheswagmi,@polkadot,stellar-sdk,@walletconnect, or@reownand cut each one.widget.lazy.tsxand its descendants should importuseNetwork,useEventsContext, or any hook that chains to a blockchain SDK.services/api/price.serviceinstead ofservices/api).@vortexfi/sharedbrowser export — point it to./dist/browser/index.jsand add"sideEffects": false. Rebuild shared before testing the frontend.Affected surfaces (per CONTEXT.md): Initial bundle tier,
widget.lazy.tsxboundary,MarketingLayout,FeeComparisonsection,@vortexfi/sharedpackage exports.