{"record":{"id":"992534bfd08fdfa3","repo":"facebook/docusaurus","slug":"hook-is-called-outside-the-docprovider","errorCode":null,"errorMessage":"Hook is called outside the <DocProvider>. ","messagePattern":"Hook is called outside the <DocProvider>\\. ","errorType":"exception","errorClass":"ReactContextError","httpStatus":null,"severity":"error","filePath":"packages/docusaurus-plugin-content-docs/src/client/doc.tsx","lineNumber":68,"sourceCode":"}: {\n  children: ReactNode;\n  content: PropDocContent;\n}): ReactNode {\n  const contextValue = useContextValue(content);\n  return <Context.Provider value={contextValue}>{children}</Context.Provider>;\n}\n\n/**\n * Returns the data of the currently browsed doc. Gives access to the doc's MDX\n * Component, front matter, metadata, TOC, etc. When swizzling a low-level\n * component (e.g. the \"Edit this page\" link) and you need some extra metadata,\n * you don't have to drill the props all the way through the component tree:\n * simply use this hook instead.\n */\nexport function useDoc(): DocContextValue {\n  const doc = useContext(Context);\n  if (doc === null) {\n    throw new ReactContextError('DocProvider');\n  }\n  return doc;\n}\n","sourceCodeStart":50,"sourceCodeEnd":72,"githubUrl":"https://github.com/facebook/docusaurus/blob/3f483e80e326cc646b54b83d564b3f0c4881b9a6/packages/docusaurus-plugin-content-docs/src/client/doc.tsx#L50-L72","documentation":"Thrown by useDoc() when DocContext is null, i.e. no <DocProvider> ancestor exists. DocProvider is set up by the docs theme (DocPage) around the rendered doc; calling useDoc outside that subtree (e.g. in a layout, navbar, or on a non-doc route) throws. ReactContextError interpolates the hook name from the stack so the message reads 'Hook useDoc is called outside the <DocProvider>.'.","triggerScenarios":"Calling useDoc() in a global layout/navbar component rendered on all pages; using a docs-internal component inside a blog or page route; swizzling that removed the DocProvider wrapper from DocPage.","commonSituations":"Reusing a doc toolbar/sidebar subcomponent in a custom layout; a swizzle of DocPage that drops the provider; importing doc-only components into MDX pages outside the docs plugin.","solutions":["Ensure useDoc() is only called within components rendered under <DocProvider> (the DocPage -> DocProvider subtree).","For components that must work on non-doc pages, use route-aware hooks like useDocsVersion() guarded by useRouteContext(), or pass data via props.","Re-add DocProvider in a swizzled DocPage if you accidentally removed it.","In tests, wrap renders in a DocProvider with the expected DocContextValue."],"exampleFix":"// before\nfunction EditUrl() {\n  const { metadata } = useDoc(); // throws on non-doc pages\n}\n// after: only render within the doc subtree, or guard the route\nfunction EditUrl() {\n  const route = useRouteContext();\n  if (route?.plugin.name !== 'docusaurus-plugin-content-docs') return null;\n  const { metadata } = useDoc();\n  return <a href={metadata.editUrl}>Edit</a>;\n}","handlingStrategy":"validation","validationCode":"import useRouteContext from '@docusaurus/useRouteContext';\n\nfunction useIsDocRoute() {\n  const route = useRouteContext();\n  return route?.plugin?.name === 'docusaurus-plugin-content-docs';\n}\n\n// if (!useIsDocRoute()) return null; // before calling useDoc()","typeGuard":"const isDocRoute = (\n  route: ReturnType<typeof useRouteContext>,\n): boolean => route?.plugin?.name === 'docusaurus-plugin-content-docs';","tryCatchPattern":null,"preventionTips":["Keep useDoc() calls within DocPage -> DocProvider subtree.","Gate shared components on route plugin name before calling doc hooks.","Re-add DocProvider in any swizzled DocPage."],"tags":["react-hook","docs","react-context","swizzle"],"backgroundTag":null,"analyzedSha":"3f483e80e326cc646b54b83d564b3f0c4881b9a6","analyzedAt":"2026-08-12T13:25:04.382Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}