Skip to content

feat(site): preserve scroll position on framework switch (ClientRouter)#609

Closed
decepulis wants to merge 1 commit intomainfrom
feat/scroll-restore-client-router
Closed

feat(site): preserve scroll position on framework switch (ClientRouter)#609
decepulis wants to merge 1 commit intomainfrom
feat/scroll-restore-client-router

Conversation

@decepulis
Copy link
Copy Markdown
Collaborator

@decepulis decepulis commented Feb 25, 2026

Summary

Preserves scroll position when switching frameworks (React ↔ HTML) on docs pages where the slug stays the same. Converts the site to an SPA hybrid using Astro's ClientRouter, which intercepts navigation and swaps DOM without a full page reload.

  • Uses navigate() from astro:transitions/client instead of window.location assignment
  • Captures scrollY before navigation, restores in .then() after DOM swap
  • Adds data-astro-rerun to ThemeInit and StyleInit so inline scripts re-execute after SPA swap
  • Adapts DocsSidebarRestoration to listen for both native and Astro lifecycle events

Bake-off companion: #608 (pagereveal approach)

Closes #484

Approach: Why ClientRouter?

Astro's ClientRouter turns navigation into DOM swaps — no full page reload means scroll position can be trivially captured and restored via a promise. Side benefits include faster navigation and smoother transitions across the entire site.

Changes

File What
Base.astro Added <ClientRouter /> to <head> — enables SPA mode site-wide
Selectors.tsx Replaced window.location with navigate() + scroll restore
JSPickerClient.tsx Same pattern; removed TODO comment
StyleInit.astro Added data-astro-rerun so html[data-style] updates after SPA swap
ThemeInit.astro Added data-astro-rerun so dark mode class persists after SPA swap
DocsSidebarRestoration.astro Extracted save/restore helpers; added astro:before-swap/astro:after-swap listeners alongside native events; added initial load restore

Risks

Risk Severity Mitigation
is:inline scripts not re-running after SPA nav High data-astro-rerun on StyleInit + ThemeInit
Safari/FilmGrain flickering Medium Kept existing view-transition-name: none on html/body
ClientRouter in Base.astro affects ALL pages Medium Blog/home get SPA nav for free; needs regression testing
Island re-hydration timing after swap Medium Test thoroughly

Test plan

  • pnpm dev, navigate to a docs page, scroll to middle, switch framework via sidebar dropdown → scroll preserved, no flicker
  • Switch framework via JSPicker on installation page → scroll preserved
  • Navigate to page where slug changes on framework switch → scrolls to top
  • Sidebar state (scroll + details open/closed) persists across navigation
  • Dark mode and style preference persist
  • Browser back/forward work correctly
  • Hard refresh works correctly
  • Navigate between non-docs pages (home → blog → docs) — no visual glitches or flicker
  • Test Safari, Chrome, Firefox

🤖 Generated with Claude Code

…uter

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@vercel
Copy link
Copy Markdown

vercel bot commented Feb 25, 2026

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

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
vjs-10-demo-react Ignored Ignored Feb 25, 2026 8:07pm

Request Review

@netlify
Copy link
Copy Markdown

netlify bot commented Feb 25, 2026

Deploy Preview for vjs10-site ready!

Name Link
🔨 Latest commit c346471
🔍 Latest deploy log https://app.netlify.com/projects/vjs10-site/deploys/699f569a53d0c900082f3342
😎 Deploy Preview https://deploy-preview-609--vjs10-site.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@github-actions
Copy link
Copy Markdown
Contributor

📦 Bundle Size Report

Package Size Diff %
@videojs/core 5.68 kB 0 B ░░░░░░░░ 0%
@videojs/element 1.60 kB 0 B ░░░░░░░░ 0%
@videojs/html 8.63 kB 0 B ░░░░░░░░ 0%
@videojs/icons 3.46 kB 0 B ░░░░░░░░ 0%
@videojs/react 15.92 kB 0 B ░░░░░░░░ 0%
@videojs/store 1.94 kB 0 B ░░░░░░░░ 0%
@videojs/utils 2.47 kB 0 B ░░░░░░░░ 0%

Total: 39.71 kB · 0 B · 0%


Entry Breakdown

Subpath sizes are the additional bytes on top of the root entry point, measured by bundling root + subpath together and subtracting the root-only size.

@videojs/core
Entry Base PR Diff %
. 3.10 kB 3.10 kB 0 B 0%
./dom 2.58 kB 2.58 kB 0 B 0%
total 5.68 kB 5.68 kB 0 B 0%
@videojs/element
Entry Base PR Diff %
. 817 B 817 B 0 B 0%
./context 823 B 823 B 0 B 0%
total 1.60 kB 1.60 kB 0 B 0%
@videojs/icons
Entry Base PR Diff %
./react 2.10 kB 2.10 kB 0 B 0%
./html 1.37 kB 1.37 kB 0 B 0%
total 3.46 kB 3.46 kB 0 B 0%
@videojs/react
Entry Base PR Diff %
. 7.69 kB 7.69 kB 0 B 0%
./audio 266 B 266 B 0 B 0%
./background 268 B 268 B 0 B 0%
./video 7.72 kB 7.72 kB 0 B 0%
total 15.92 kB 15.92 kB 0 B 0%
@videojs/store
Entry Base PR Diff %
. 1.29 kB 1.29 kB 0 B 0%
./html 468 B 468 B 0 B 0%
./react 199 B 199 B 0 B 0%
total 1.94 kB 1.94 kB 0 B 0%
@videojs/utils
Entry Base PR Diff %
./array 104 B 104 B 0 B 0%
./dom 684 B 684 B 0 B 0%
./events 227 B 227 B 0 B 0%
./function 197 B 197 B 0 B 0%
./object 119 B 119 B 0 B 0%
./predicate 265 B 265 B 0 B 0%
./string 110 B 110 B 0 B 0%
./style 185 B 185 B 0 B 0%
./time 478 B 478 B 0 B 0%
./number 158 B 158 B 0 B 0%
total 2.47 kB 2.47 kB 0 B 0%

ℹ️ How to interpret

Sizes are minified + brotli, measured with esbuild.
Package totals are computed as root size + marginal subpath costs.
Subpath marginal cost = (root + subpath bundled together) − root alone.

Icon Meaning
No change
🔺 Increased ≤ 10%
🔴 Increased > 10%
🔽 Decreased
🆕 New (no baseline)

Run pnpm size locally to check current sizes.

@decepulis
Copy link
Copy Markdown
Collaborator Author

We have some scripts that run in head that assume a full page navigation. More work is required before we switch to a SPA app. For now, #608 will address this issue!

@decepulis decepulis closed this Feb 25, 2026
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.

fix(docs): jump to top of window when toggling between frameworks

1 participant