facebook/docusaurus · error · ReactContextError

Hook ${hookName} is called outside the <TitleFormatterProvid

Error message

Hook ${hookName} is called outside the <TitleFormatterProvider>.

What it means

useTitleFormatter() reads TitleFormatterContext via useTitleFormatterContext; when the context value is null (no TitleFormatterProvider ancestor), it throws a ReactContextError. The hook name in the message is parsed from the call stack, so the same generic error reports whichever consuming hook (useTitleFormatter, useTitleFormatterContext) was invoked outside the provider.

Source

Thrown at packages/docusaurus-theme-common/src/utils/titleFormatterUtils.tsx:95

export function TitleFormatterProvider({
  formatter,
  children,
}: {
  children: ReactNode;
  formatter: TitleFormatterFnWithDefault;
}): ReactNode {
  return (
    <TitleFormatterContext.Provider value={formatter}>
      {children}
    </TitleFormatterContext.Provider>
  );
}

function useTitleFormatterContext() {
  const value = useContext(TitleFormatterContext);
  if (value === null) {
    throw new ReactContextError('TitleFormatterProvider');
  }
  return value;
}

/**
 * Returns a function to format the page title
 */
export function useTitleFormatter(): TitleFormatter {
  const formatter = useTitleFormatterContext();
  const {siteConfig} = useDocusaurusContext();
  const {title: siteTitle, titleDelimiter} = siteConfig;

  // Unfortunately we can only call this hook here, not in the provider
  // Route context can't be accessed in any provider applied above the router
  const {plugin} = useRouteContext();

  return {
    format: (title: string) =>

View on GitHub (pinned to 3f483e80e3)

Solutions

  1. Keep the default theme provider chain intact so TitleFormatterProvider wraps the router.
  2. If you swizzle the root, re-add <TitleFormatterProvider formatter={...}> around your tree (use the default formatter from TitleFormatterFnDefault).
  3. Only call useTitleFormatter() from components rendered within a routed page.

Example fix

// before (custom root, provider removed)
export default function Root({children}) {
  return <>{children}</>;
}
// after
import {TitleFormatterProvider, TitleFormatterFnDefault} from '@docusaurus/theme-common/internal';
export default function Root({children}) {
  return (
    <TitleFormatterProvider formatter={TitleFormatterFnDefault}>
      {children}
    </TitleFormatterProvider>
  );
}
Defensive patterns

Strategy: type-guard

Prevention

When it happens

Trigger: Call useTitleFormatter() in a component rendered above TitleFormatterProvider; customize the theme root layout and omit or reorder the provider; call the hook in a context that is not a route child (provider is applied within the router tree).

Common situations: Swizzle/customize ThemeRoot or Layout and drop TitleFormatterProvider; using the hook in a top-level provider that sits above where Docusaurus injects it; calling the formatter outside React render (it is a hook).

Related errors


AI-assisted analysis of facebook/docusaurus@3f483e80e3 (2026-08-12). Data as JSON: /api/errors/83d623f00150aced. Report an issue: GitHub.