Problem
The sidebar navigation currently resets to the top every time the user navigates between pages.
For readers working through content in sections deep in the sidebar (e.g. Node-API, Diagnostics,
or Asynchronous Work), this means manually scrolling back down after every page visit — which creates
a frustrating and disorienting experience.
Additionally, SideBar from @node-core/ui-components does not support forwardRef, so passing
ref={sidebarRef} to it is silently ignored. This means the scroll restoration logic in
useScrollToElement and useScroll never receives a valid DOM reference to attach the scroll listener to.
Proposed Solution
-
Fix ref acquisition – Use useLayoutEffect in Sidebar/index.jsx to manually assign sidebarRef.current
to the rendered <aside> element before useEffects run, ensuring the scroll listener is properly attached.
-
Persist scroll position – Extend useScrollToElement to:
- Save the sidebar scroll position to
localStorage on every scroll event (debounced).
- On mount, restore from
NavigationStateContext (same-session navigation) or fall back to localStorage
(full page refresh), whichever is available.
Files Changed
components/Sidebar/index.jsx – use useLayoutEffect to acquire ref to <aside>
hooks/useScrollToElement.js – add localStorage read on mount and write on scroll
hooks/useScroll.js – use onScrollRef pattern to avoid stale closure on onScroll callback
Expected Behaviour
| Action |
Before |
After |
| Navigate to another page |
Sidebar resets to top |
Sidebar stays at last scroll position |
| Hard refresh (F5) |
Sidebar resets to top |
Sidebar restores from localStorage |
Notes
SideBar from @node-core/ui-components needs to support forwardRef for this to work without
the useLayoutEffect workaround. A separate issue/PR upstream on that package may be worth considering.
- The
useScroll hook dependency on ref.current (instead of ref) ensures the listener is attached
after the DOM element is available.
Problem
The sidebar navigation currently resets to the top every time the user navigates between pages.
For readers working through content in sections deep in the sidebar (e.g. Node-API, Diagnostics,
or Asynchronous Work), this means manually scrolling back down after every page visit — which creates
a frustrating and disorienting experience.
Additionally,
SideBarfrom@node-core/ui-componentsdoes not supportforwardRef, so passingref={sidebarRef}to it is silently ignored. This means the scroll restoration logic inuseScrollToElementanduseScrollnever receives a valid DOM reference to attach the scroll listener to.Proposed Solution
Fix ref acquisition – Use
useLayoutEffectinSidebar/index.jsxto manually assignsidebarRef.currentto the rendered
<aside>element beforeuseEffects run, ensuring the scroll listener is properly attached.Persist scroll position – Extend
useScrollToElementto:localStorageon every scroll event (debounced).NavigationStateContext(same-session navigation) or fall back tolocalStorage(full page refresh), whichever is available.
Files Changed
components/Sidebar/index.jsx– useuseLayoutEffectto acquire ref to<aside>hooks/useScrollToElement.js– add localStorage read on mount and write on scrollhooks/useScroll.js– useonScrollRefpattern to avoid stale closure ononScrollcallbackExpected Behaviour
Notes
SideBarfrom@node-core/ui-componentsneeds to supportforwardReffor this to work withoutthe
useLayoutEffectworkaround. A separate issue/PR upstream on that package may be worth considering.useScrollhook dependency onref.current(instead ofref) ensures the listener is attachedafter the DOM element is available.