perf(admin): lazy-load route pages#1666
Conversation
Wrap the admin route pages in React.lazy with a Suspense boundary in the layout, so each page ships as its own chunk instead of one eager bundle. Page components are now default exports, enabling the canonical lazy(() => import(...)) form. Trims per-page code (~105 KB gz) off the initial/login bundle. App-only change — no SDK or public-API changes. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (1)
📝 WalkthroughSummary by CodeRabbit
WalkthroughAdds a Suspense boundary in the admin App layout, converts multiple admin page modules to default exports, and updates routes to load those pages with React.lazy while keeping the app shell and useAdminPaths eagerly imported. ChangesCode-splitting route pages
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Possibly related PRs
Suggested reviewers
🚥 Pre-merge checks | ✅ 2✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Coverage Report for CI Build 26947489994Coverage remained the same at 43.221%Details
Uncovered ChangesNo uncovered changes found. Coverage RegressionsNo coverage regressions found. Coverage Stats
💛 - Coveralls |
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
web/apps/admin/src/pages/organizations/details/index.tsx (1)
66-66:⚠️ Potential issue | 🟠 Major | ⚡ Quick winWrap the nested
<Outlet />in a local Suspense boundary to avoid replacing the whole org details shell on lazy-tab load.
routes.tsxlazy-loads the org child views, andOrganizationDetailsViewrenders{children}directly (no Suspense boundary). Without a local boundary around the nested<Outlet />, the suspension bubbles to the top-level Suspense inApp.tsx, which swaps out the entire routed content (including the org header/tabs) withLoadingStateuntil the chunk arrives. A local boundary confines the fallback to the nested tab area.♻️ Proposed local boundary
-import { useCallback, useContext, useEffect, useState } from 'react'; +import { Suspense, useCallback, useContext, useEffect, useState } from 'react'; +import LoadingState from '~/components/states/Loading';- <Outlet /> + <Suspense fallback={<LoadingState />}> + <Outlet /> + </Suspense>
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: c6fe09ed-4188-4078-b5f8-d949fa6df30f
📒 Files selected for processing (14)
web/apps/admin/src/App.tsxweb/apps/admin/src/pages/admins/AdminsPage.tsxweb/apps/admin/src/pages/audit-logs/AuditLogsPage.tsxweb/apps/admin/src/pages/invoices/InvoicesPage.tsxweb/apps/admin/src/pages/organizations/details/index.tsxweb/apps/admin/src/pages/organizations/list/index.tsxweb/apps/admin/src/pages/plans/PlansPage.tsxweb/apps/admin/src/pages/preferences/PreferencesPage.tsxweb/apps/admin/src/pages/products/ProductPricesPage.tsxweb/apps/admin/src/pages/products/ProductsPage.tsxweb/apps/admin/src/pages/roles/RolesPage.tsxweb/apps/admin/src/pages/users/UsersPage.tsxweb/apps/admin/src/pages/webhooks/WebhooksPage.tsxweb/apps/admin/src/routes.tsx
The 7 organization-detail child views come from the @raystack/frontier/admin barrel, which the app shell already imports statically — so the barrel is pinned into the initial chunk. Wrapping those views in React.lazy produced no separate chunk (verified: no Organization*View chunks are emitted) and only added Suspense churn, so import them statically instead. lazy() is kept on the 12 app-owned page files, which do split into their own chunks. Initial bundle is unchanged (~638 KB gz). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Lazy-loads the admin route pages so each ships as its own chunk rather than one eager bundle.
React.lazyper route + aSuspenseboundary in the layout (sidebar stays mounted while a page chunk loads).lazy(() => import(...))form.Further savings (follow-up)
There's a bigger scope that this PR doesn't tackle: deferring the ~245 KB gz
@raystack/frontier/adminview barrel off the initial load. We can't do that yet because the app shell (sidebar, config provider, route slugs) eagerly imports a few lightweight hooks from the same barrel that also holds all the views. That one eager import pins the whole barrel into the initial chunk, so thelazy()calls can't actually move the views out.To unlock it, we'd need a view-free SDK subpath that exposes just those config and hook exports, leaving the view barrel reachable only through dynamic imports.
We're planning to do exactly that while segregating the shared paths (the upcoming
/sharedrefactor), so the non-view exports move into their own entry and the view barrel can finally split into its own deferred chunk.