Skip to content

Show loading spinner throughout the full app boot sequence#11608

Merged
nbudin merged 1 commit into
mainfrom
moar-spinnar
Jun 4, 2026
Merged

Show loading spinner throughout the full app boot sequence#11608
nbudin merged 1 commit into
mainfrom
moar-spinnar

Conversation

@nbudin
Copy link
Copy Markdown
Contributor

@nbudin nbudin commented Jun 4, 2026

Purpose

There were two gaps in loading feedback during app boot: a blank screen before the JS bundle downloaded, and a ~1 second blank screen between the bootstrap Suspense spinner disappearing and the route components appearing on screen.

The root cause of the second gap was subtle: createBrowserRouter starts the initial navigation immediately when called, but router.state.initialized is false until the navigation completes. During that window, RouterProvider renders nothing visible because the lazy root module and its loaders haven't finished yet — and the existing Suspense spinner had already gone away.

Changes

💻 Engineer-facing

  • Inline HTML spinner in the SPA shell and CMS-rendered AppRoot so there's something on screen as soon as the HTML parses, before any JS runs. Extracted into ApplicationHelper#app_root_loading_content so both cdn_spa_shell.html.erb and CmsRenderingContext use the same markup.
  • RouterLoadingOverlay component rendered alongside RouterProvider. It subscribes to router.state.initialized via useLayoutEffect (runs before RouterProvider's own layout effect, so it sees the buffered post-navigation state) and removes itself once the router is ready. No full-page overlay — just the same Bootstrap spinner, in normal flow, so it disappears at the same time content appears.

Testing

Tested locally across cold loads and navigations. The spinner now appears immediately and stays visible until the page is ready.

🚢

🤖 Generated with Claude Code

- Inline CSS spinner in the HTML shell so something appears immediately,
  even before the JS bundle downloads
- RouterLoadingOverlay component that stays visible until
  router.state.initialized is true, bridging the gap between the
  bootstrap Suspense phase and route components rendering
- app_root_loading_content helper in ApplicationHelper (included by both
  the ERB layout and CmsRenderingContext) so the inline spinner is
  rendered consistently in both paths

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@nbudin nbudin marked this pull request as ready for review June 4, 2026 03:31
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Jun 4, 2026

Code Coverage Report: Only Changed Files listed

Package Base Coverage New Coverage Difference
app/helpers/application_helper.rb 🟢 82.61% 🟢 85.19% 🟢 2.58%
app/javascript/BuiltInFormControls/AddFileModal.tsx 🔴 33.33% 🔴 0% 🔴 -33.33%
app/javascript/BuiltInFormControls/LiquidInput.tsx 🟠 60% 🟠 57.5% 🔴 -2.5%
app/javascript/setupI18Next.ts 🟠 72.41% 🟠 65.52% 🔴 -6.89%
Overall Coverage 🟢 52.82% 🟢 52.78% 🔴 -0.04%

Minimum allowed coverage is 0%, this run produced 52.78%

@nbudin nbudin merged commit 6c9c2b6 into main Jun 4, 2026
18 checks passed
@nbudin nbudin deleted the moar-spinnar branch June 4, 2026 15:25
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.

1 participant