facebook/docusaurus · error · ReactContextError
Hook is called outside the <ScrollControllerProvider>.
Error message
Hook is called outside the <ScrollControllerProvider>.
What it means
Thrown by `useScrollController()` when its context is `null`, i.e. the hook was called outside `<ScrollControllerProvider>`. The provider tracks scroll position and exposes pause/resume controls used by the navbar and back-to-top button to ignore programmatic scrolls.
Source
Thrown at packages/docusaurus-theme-common/src/utils/scrollUtils.tsx:74
const value = useScrollControllerContextValue();
return (
<ScrollMonitorContext.Provider value={value}>
{children}
</ScrollMonitorContext.Provider>
);
}
/**
* We need a way to update the scroll position while ignoring scroll events
* so as not to toggle Navbar/BackToTop visibility.
*
* This API permits to temporarily disable/ignore scroll events. Motivated by
* https://github.com/facebook/docusaurus/pull/5618
*/
export function useScrollController(): ScrollController {
const context = useContext(ScrollMonitorContext);
if (context == null) {
throw new ReactContextError('ScrollControllerProvider');
}
return context;
}
type ScrollPosition = {scrollX: number; scrollY: number};
const getScrollPosition = (): ScrollPosition | null =>
ExecutionEnvironment.canUseDOM
? {
scrollX: window.pageXOffset,
scrollY: window.pageYOffset,
}
: null;
/**
* This hook fires an effect when the scroll position changes. The effect will
* be provided with the before/after scroll positions. Note that the effect may
* not be always run: if scrolling is disabled through `useScrollController`, itView on GitHub (pinned to 3f483e80e3)
Solutions
- Keep `<ScrollControllerProvider>` as a high ancestor (near the layout root) wrapping navbar and back-to-top button.
- In tests, wrap consuming components with the provider.
- Restore the provider in any swizzled layout; verify against upstream after upgrades.
Example fix
// before — navbar rendered outside provider
<Navbar />
<ScrollControllerProvider>{children}</ScrollControllerProvider>
// after
<ScrollControllerProvider>
<Navbar />
{children}
</ScrollControllerProvider> Defensive patterns
Strategy: validation
Validate before calling
import {useContext} from 'react';
import {ScrollMonitorContext} from '@docusaurus/theme-common/internal';
function useScrollControllerSafe() {
return useContext(ScrollMonitorContext); // null if outside provider
} Try / catch
try {
const scroll = useScrollController();
} catch (e) {
if (e instanceof Error && e.message.includes('ScrollControllerProvider')) return null;
throw e;
} Prevention
- Mount `<ScrollControllerProvider>` near the layout root, above navbar and back-to-top button.
- Wrap test renders of scroll consumers with the provider.
- Restore the provider in swizzled layouts.
When it happens
Trigger: A component consuming `useScrollController` (directly or via `useScrollPosition`/`useScrollListener`) is rendered above/outside the provider in the React tree. The provider is normally mounted near the layout root.
Common situations: Swizzling `Layout`/`Navbar` and dropping or relocating `<ScrollControllerProvider>`; rendering the navbar or back-to-top button in a separate root/portal that escapes the provider; isolated tests of scroll-consuming components without the provider.
Related errors
- Hook is called outside the <NavbarMobileSidebarProvider>.
- Hook is called outside the <NavbarSecondaryMenuContentProvid
- Hook is called outside the <NavbarSecondaryMenuDisplayProvid
- Hook is called outside the <AnnouncementBarProvider>.
- Hook is called outside the <ColorModeProvider>. Please see h
AI-assisted analysis of facebook/docusaurus@3f483e80e3 (2026-08-12).
Data as JSON: /api/errors/1cbf75a85fbe89b1.
Report an issue: GitHub.