Skip to content

perf(admin): lazy-load route pages#1666

Merged
paanSinghCoder merged 3 commits into
mainfrom
lazy-load-admin-routes
Jun 5, 2026
Merged

perf(admin): lazy-load route pages#1666
paanSinghCoder merged 3 commits into
mainfrom
lazy-load-admin-routes

Conversation

@paanSinghCoder
Copy link
Copy Markdown
Contributor

@paanSinghCoder paanSinghCoder commented Jun 2, 2026

Lazy-loads the admin route pages so each ships as its own chunk rather than one eager bundle.

  • React.lazy per route + a Suspense boundary in the layout (sidebar stays mounted while a page chunk loads).
  • Page components converted to default exports for the canonical lazy(() => import(...)) form.
  • ~105 KB gz off the initial bundle; the unauthenticated login screen no longer downloads page code.
  • App-only change — no SDK or public-API changes.

Further savings (follow-up)

There's a bigger scope that this PR doesn't tackle: deferring the ~245 KB gz @raystack/frontier/admin view 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 the lazy() 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 /shared refactor), so the non-view exports move into their own entry and the view barrel can finally split into its own deferred chunk.

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>
@vercel
Copy link
Copy Markdown

vercel Bot commented Jun 2, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
frontier Ready Ready Preview, Comment Jun 4, 2026 10:57am

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Jun 2, 2026

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: c79ae7c6-54e9-4e88-8a70-f6581f428086

📥 Commits

Reviewing files that changed from the base of the PR and between bf6e07a and 6736ce9.

📒 Files selected for processing (1)
  • web/apps/admin/src/routes.tsx

📝 Walkthrough

Summary by CodeRabbit

  • Refactor
    • Admin pages now load lazily to improve performance and reduce initial load time.
  • New Features
    • Added a global loading indicator during route transitions that keeps the sidebar visible while pages load, improving perceived responsiveness.

Walkthrough

Adds 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.

Changes

Code-splitting route pages

Layer / File(s) Summary
Suspense boundary for lazy loading in layout
web/apps/admin/src/App.tsx
App.tsx imports Suspense and LoadingState, then wraps the routed Outlet in a Suspense boundary with LoadingState as the fallback. A comment notes the boundary sits within the layout so the sidebar remains mounted during lazy route page loading.
Page component export conversions
web/apps/admin/src/pages/admins/AdminsPage.tsx, web/apps/admin/src/pages/audit-logs/AuditLogsPage.tsx, web/apps/admin/src/pages/invoices/InvoicesPage.tsx, web/apps/admin/src/pages/organizations/details/index.tsx, web/apps/admin/src/pages/organizations/list/index.tsx, web/apps/admin/src/pages/plans/PlansPage.tsx, web/apps/admin/src/pages/preferences/PreferencesPage.tsx, web/apps/admin/src/pages/products/ProductPricesPage.tsx, web/apps/admin/src/pages/products/ProductsPage.tsx, web/apps/admin/src/pages/roles/RolesPage.tsx, web/apps/admin/src/pages/users/UsersPage.tsx, web/apps/admin/src/pages/webhooks/WebhooksPage.tsx
Twelve page components change from named exports to default exports to support React.lazy imports. Component implementations and props remain unchanged.
Route configuration refactoring to lazy loading
web/apps/admin/src/routes.tsx
Top-level imports add lazy from React; the app/auth shell imports remain eager. Main admin page components are replaced with React.lazy declarations that load module defaults; useAdminPaths remains an eager hook import.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

  • raystack/frontier#1375: The AdminsPage export change and route wiring relate to earlier edits touching the AdminsPage and super-admins route.

Suggested reviewers

  • rohanchkrabrty
🚥 Pre-merge checks | ✅ 2
✅ Passed checks (2 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ 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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coveralls
Copy link
Copy Markdown

coveralls commented Jun 2, 2026

Coverage Report for CI Build 26947489994

Coverage remained the same at 43.221%

Details

  • Coverage remained the same as the base build.
  • Patch coverage: No coverable lines changed in this PR.
  • No coverage regressions found.

Uncovered Changes

No uncovered changes found.

Coverage Regressions

No coverage regressions found.


Coverage Stats

Coverage Status
Relevant Lines: 38046
Covered Lines: 16444
Line Coverage: 43.22%
Coverage Strength: 12.14 hits per line

💛 - Coveralls

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

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 win

Wrap the nested <Outlet /> in a local Suspense boundary to avoid replacing the whole org details shell on lazy-tab load.

routes.tsx lazy-loads the org child views, and OrganizationDetailsView renders {children} directly (no Suspense boundary). Without a local boundary around the nested <Outlet />, the suspension bubbles to the top-level Suspense in App.tsx, which swaps out the entire routed content (including the org header/tabs) with LoadingState until 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

📥 Commits

Reviewing files that changed from the base of the PR and between 0e8b178 and bf6e07a.

📒 Files selected for processing (14)
  • web/apps/admin/src/App.tsx
  • web/apps/admin/src/pages/admins/AdminsPage.tsx
  • web/apps/admin/src/pages/audit-logs/AuditLogsPage.tsx
  • web/apps/admin/src/pages/invoices/InvoicesPage.tsx
  • web/apps/admin/src/pages/organizations/details/index.tsx
  • web/apps/admin/src/pages/organizations/list/index.tsx
  • web/apps/admin/src/pages/plans/PlansPage.tsx
  • web/apps/admin/src/pages/preferences/PreferencesPage.tsx
  • web/apps/admin/src/pages/products/ProductPricesPage.tsx
  • web/apps/admin/src/pages/products/ProductsPage.tsx
  • web/apps/admin/src/pages/roles/RolesPage.tsx
  • web/apps/admin/src/pages/users/UsersPage.tsx
  • web/apps/admin/src/pages/webhooks/WebhooksPage.tsx
  • web/apps/admin/src/routes.tsx

Comment thread web/apps/admin/src/routes.tsx Outdated
@paanSinghCoder paanSinghCoder self-assigned this Jun 2, 2026
Comment thread web/apps/admin/src/routes.tsx Outdated
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>
@paanSinghCoder paanSinghCoder merged commit 3430fcd into main Jun 5, 2026
8 checks passed
@paanSinghCoder paanSinghCoder deleted the lazy-load-admin-routes branch June 5, 2026 04:34
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.

3 participants