Problem
The dependency list in package.json includes several sizeable libraries relevant to bundle size — @stellar/stellar-sdk, stellar-plus, soroban-client (note: both @stellar/stellar-sdk and the older, separate soroban-client package are dependencies simultaneously — worth confirming whether soroban-client is genuinely still needed or is dead weight duplicating functionality now in @stellar/stellar-sdk), recharts, framer-motion, @chakra-ui/react + @emotion/react/@emotion/styled. None of these are code-split or dynamically imported anywhere in src/ (a quick audit shows every page-level component statically imports its full dependency tree), and there is no next build bundle-analysis step, size-limit check, or CI gate anywhere in package.json's scripts or any config file that would catch a regression (e.g. someone accidentally importing all of recharts into a page that doesn't need charts, or a new dependency ballooning the shared chunk that every route pays for, including the initial / dashboard load).
This matters concretely for this app: TvlChart.tsx pulls in recharts for a single area chart used only on the pool-detail page, but nothing prevents recharts from ending up in a shared chunk loaded by pages that never render a chart. Similarly, @stellar/stellar-sdk is a large, XDR/crypto-heavy dependency imported at module scope by src/lib/soroban.ts, which is in turn imported by nearly every page (Farm, PoolDetailClient, HistoryPage, LeaderboardPage via useLeaderboard) — meaning the full SDK likely ships in the initial bundle for pages like /prices, /airdrops, /webhooks, /alerts that don't need any Soroban functionality at all (they only call src/lib/backend.ts, a lightweight fetch wrapper with zero SDK dependency).
Acceptance Criteria
Relevant Files
package.json — lists both @stellar/stellar-sdk and soroban-client as dependencies with no bundle-analysis tooling configured
src/lib/soroban.ts — imported by nearly every page, pulling the full Stellar SDK into routes that may not need it
src/components/TvlChart/TvlChart.tsx — the only consumer of recharts, worth verifying is code-split away from pages that don't render a chart
src/app/prices/page.tsx — an example page that only needs src/lib/backend.ts and should not need the Soroban SDK in its bundle
Further Investigation
Re-checked package.json directly: confirmed @stellar/stellar-sdk (^16.0.1), stellar-plus (^0.14.4), soroban-client (^1.0.1), recharts (^3.9.0), framer-motion (^11.11.17), and @chakra-ui/react (^2.10.4) are all present as dependencies, and the scripts block (dev, dev:stack, build, start, lint, test, playwright) has no bundle-analysis, size-limit, or CI budget step of any kind — this fully confirms the issue as filed. Also confirmed via grep that soroban-client has zero import sites anywhere in src/ (grep -rln "from ['\"]soroban-client['\"]" src returns nothing) while recharts has exactly one consumer (src/components/TvlChart/TvlChart.tsx) — both facts the original body only hedged as "worth confirming"; they can now be stated as confirmed findings, not open questions.
This remains correctly labeled spike: the destination (some kind of bundle budget + code-splitting) is easy to state, but the right mechanism is genuinely undetermined — Next.js version, App Router data-fetching boundaries, and whether route-level code-splitting can actually isolate @stellar/stellar-sdk out of shared chunks all require investigation before committing to an approach, and getting it wrong (e.g. picking a size-limit tool that doesn't understand Next's per-route chunking) could produce a CI check that's either too noisy or blind to the exact regression it's meant to catch.
Tradeoff approaches
@next/bundle-analyzer + manual baseline in a doc/comment, no CI gate. Lowest effort, immediately actionable, gives visibility via a locally-run ANALYZE=true next build. Downside: doesn't actually prevent regressions, just makes them visible to whoever remembers to run it — likely to bit-rot.
next build's own .next/analyze or build output JSON, parsed by a small custom script, checked into CI with a hard per-route byte budget. More setup work (need to pick which routes matter, set thresholds, handle first-load-JS vs. total-JS distinction Next reports), but gives an actual enforced gate. Risk: threshold-tuning churn and false positives on legitimate growth (e.g. adding a real feature) requiring a documented process for bumping the budget deliberately.
size-limit (or equivalent) per-entry-point config, most precise but Next.js's App Router doesn't produce simple single-entry bundles the way size-limit traditionally expects — would likely require size-limit's Next.js-specific plugin or a custom webpack-stats parsing step, higher setup cost, but best long-term ergonomics and easiest to explain to contributors ("this route's budget is X KB").
- Route-level dynamic imports for
soroban.ts/recharts first, budget tooling second. Attacks the actual bloat directly (the /prices, /airdrops, /webhooks, /alerts pages likely don't need the Stellar SDK at all) before investing in measurement tooling — arguably the higher-leverage first step since it fixes today's bloat regardless of which measurement tool is chosen later, and re-measures the "is this actually shared-chunk-bound" question the original body flags as unconfirmed.
Recommended sequencing regardless of which measurement tool wins: do a manual next build output read first (no tooling investment) to establish whether @stellar/stellar-sdk is actually leaking into non-Soroban routes' first-load JS today, since that finding determines whether approach 4 is even necessary before approach 1/2/3 is chosen.
Test plan
- Manual spike output: run
next build locally, capture per-route "First Load JS" table from the build output, and record which of /prices, /airdrops, /webhooks, /alerts show a first-load size consistent with pulling in @stellar/stellar-sdk (a multi-hundred-KB jump vs. a route that only imports src/lib/backend.ts) — this single manual step resolves the biggest open unknown in the issue before any tooling is chosen.
- Once a tool is chosen: a CI job asserting each documented route budget, with an intentionally-regressed test PR (temporarily import
recharts into /prices) used to verify the gate actually fails before merging the real gate.
- Confirm
soroban-client removal (already established as dead via the grep above) doesn't break npm run build/npm test — this part is not a spike and could be split into its own quick non-spike follow-up if desired, since it's a confirmed zero-risk deletion independent of the bigger tooling question.
Cross-references
Problem
The dependency list in
package.jsonincludes several sizeable libraries relevant to bundle size —@stellar/stellar-sdk,stellar-plus,soroban-client(note: both@stellar/stellar-sdkand the older, separatesoroban-clientpackage are dependencies simultaneously — worth confirming whethersoroban-clientis genuinely still needed or is dead weight duplicating functionality now in@stellar/stellar-sdk),recharts,framer-motion,@chakra-ui/react+@emotion/react/@emotion/styled. None of these are code-split or dynamically imported anywhere insrc/(a quick audit shows every page-level component statically imports its full dependency tree), and there is nonext buildbundle-analysis step, size-limit check, or CI gate anywhere inpackage.json's scripts or any config file that would catch a regression (e.g. someone accidentally importing all ofrechartsinto a page that doesn't need charts, or a new dependency ballooning the shared chunk that every route pays for, including the initial/dashboard load).This matters concretely for this app:
TvlChart.tsxpulls inrechartsfor a single area chart used only on the pool-detail page, but nothing preventsrechartsfrom ending up in a shared chunk loaded by pages that never render a chart. Similarly,@stellar/stellar-sdkis a large, XDR/crypto-heavy dependency imported at module scope bysrc/lib/soroban.ts, which is in turn imported by nearly every page (Farm,PoolDetailClient,HistoryPage,LeaderboardPageviauseLeaderboard) — meaning the full SDK likely ships in the initial bundle for pages like/prices,/airdrops,/webhooks,/alertsthat don't need any Soroban functionality at all (they only callsrc/lib/backend.ts, a lightweightfetchwrapper with zero SDK dependency).Acceptance Criteria
@next/bundle-analyzerornext build's built-in output stats) to the build/CI pipeline and record a baseline per-route JS size./prices,/airdrops,/webhooks,/alerts(which only usesrc/lib/backend.ts) are pulling in@stellar/stellar-sdk/rechartstransitively via shared layout/chunk boundaries, and if so, restructure imports (dynamicimport(), route-level code splitting) so they don't.soroban-clientpackage is still used anywhere, or is dead weight alongside@stellar/stellar-sdk; remove it if unused.Relevant Files
package.json— lists both @stellar/stellar-sdk and soroban-client as dependencies with no bundle-analysis tooling configuredsrc/lib/soroban.ts— imported by nearly every page, pulling the full Stellar SDK into routes that may not need itsrc/components/TvlChart/TvlChart.tsx— the only consumer of recharts, worth verifying is code-split away from pages that don't render a chartsrc/app/prices/page.tsx— an example page that only needs src/lib/backend.ts and should not need the Soroban SDK in its bundleFurther Investigation
Re-checked
package.jsondirectly: confirmed@stellar/stellar-sdk(^16.0.1),stellar-plus(^0.14.4),soroban-client(^1.0.1),recharts(^3.9.0),framer-motion(^11.11.17), and@chakra-ui/react(^2.10.4) are all present as dependencies, and thescriptsblock (dev,dev:stack,build,start,lint,test,playwright) has no bundle-analysis, size-limit, or CI budget step of any kind — this fully confirms the issue as filed. Also confirmed via grep thatsoroban-clienthas zero import sites anywhere insrc/(grep -rln "from ['\"]soroban-client['\"]" srcreturns nothing) whilerechartshas exactly one consumer (src/components/TvlChart/TvlChart.tsx) — both facts the original body only hedged as "worth confirming"; they can now be stated as confirmed findings, not open questions.This remains correctly labeled
spike: the destination (some kind of bundle budget + code-splitting) is easy to state, but the right mechanism is genuinely undetermined — Next.js version, App Router data-fetching boundaries, and whether route-level code-splitting can actually isolate@stellar/stellar-sdkout of shared chunks all require investigation before committing to an approach, and getting it wrong (e.g. picking a size-limit tool that doesn't understand Next's per-route chunking) could produce a CI check that's either too noisy or blind to the exact regression it's meant to catch.Tradeoff approaches
@next/bundle-analyzer+ manual baseline in a doc/comment, no CI gate. Lowest effort, immediately actionable, gives visibility via a locally-runANALYZE=true next build. Downside: doesn't actually prevent regressions, just makes them visible to whoever remembers to run it — likely to bit-rot.next build's own.next/analyzeor build output JSON, parsed by a small custom script, checked into CI with a hard per-route byte budget. More setup work (need to pick which routes matter, set thresholds, handle first-load-JS vs. total-JS distinction Next reports), but gives an actual enforced gate. Risk: threshold-tuning churn and false positives on legitimate growth (e.g. adding a real feature) requiring a documented process for bumping the budget deliberately.size-limit(or equivalent) per-entry-point config, most precise but Next.js's App Router doesn't produce simple single-entry bundles the waysize-limittraditionally expects — would likely requiresize-limit's Next.js-specific plugin or a custom webpack-stats parsing step, higher setup cost, but best long-term ergonomics and easiest to explain to contributors ("this route's budget is X KB").soroban.ts/rechartsfirst, budget tooling second. Attacks the actual bloat directly (the/prices,/airdrops,/webhooks,/alertspages likely don't need the Stellar SDK at all) before investing in measurement tooling — arguably the higher-leverage first step since it fixes today's bloat regardless of which measurement tool is chosen later, and re-measures the "is this actually shared-chunk-bound" question the original body flags as unconfirmed.Recommended sequencing regardless of which measurement tool wins: do a manual
next buildoutput read first (no tooling investment) to establish whether@stellar/stellar-sdkis actually leaking into non-Soroban routes' first-load JS today, since that finding determines whether approach 4 is even necessary before approach 1/2/3 is chosen.Test plan
next buildlocally, capture per-route "First Load JS" table from the build output, and record which of/prices,/airdrops,/webhooks,/alertsshow a first-load size consistent with pulling in@stellar/stellar-sdk(a multi-hundred-KB jump vs. a route that only importssrc/lib/backend.ts) — this single manual step resolves the biggest open unknown in the issue before any tooling is chosen.rechartsinto/prices) used to verify the gate actually fails before merging the real gate.soroban-clientremoval (already established as dead via the grep above) doesn't breaknpm run build/npm test— this part is not a spike and could be split into its own quick non-spike follow-up if desired, since it's a confirmed zero-risk deletion independent of the bigger tooling question.Cross-references
SorobanServicede-duplication) and useSorobanEvents permanently stalls real-time invalidation after a single failed getEvents call #72 (useSorobanEventsretention bug) both live insrc/lib/soroban.ts/src/hooks/useSorobanQuery.ts, which is exactly the module this issue identifies as the likely source of unwanted bundle weight in non-Soroban routes — any route-level code-splitting work done here should be coordinated with those issues' authors so a refactor ofsoroban.ts's internals (e.g. splitting simulation logic out) doesn't fight this issue's import-boundary changes.