facebook/docusaurus · error · ReactContextError

Hook is called outside the <BlogPostProvider>.

Error message

Hook is called outside the <BlogPostProvider>. 

What it means

Thrown by useBlogPost() when the React Context it reads (BlogPostContext) is still its default null value, meaning no <BlogPostProvider> ancestor rendered above the component. The message is built by ReactContextError, which interpolates the calling hook's name from the stack trace, producing 'Hook useBlogPost is called outside the <BlogPostProvider>.'. BlogPostProvider is rendered by the blog post theme components; calling useBlogPost outside that subtree is a component-tree placement error.

Source

Thrown at packages/docusaurus-plugin-content-blog/src/client/contexts.tsx:92

  children: ReactNode;
  content: PropBlogPostContent;
  isBlogPostPage?: boolean;
}): ReactNode {
  const contextValue = useContextValue({content, isBlogPostPage});
  return <Context.Provider value={contextValue}>{children}</Context.Provider>;
}

/**
 * Returns the data of the currently browsed blog post. Gives access to
 * front matter, metadata, TOC, etc.
 * When swizzling a low-level component (e.g. the "Edit this page" link)
 * and you need some extra metadata, you don't have to drill the props
 * all the way through the component tree: simply use this hook instead.
 */
export function useBlogPost(): BlogPostContextValue {
  const blogPost = useContext(Context);
  if (blogPost === null) {
    throw new ReactContextError('BlogPostProvider');
  }
  return blogPost;
}

View on GitHub (pinned to 3f483e80e3)

Solutions

  1. Move the useBlogPost() call (or the component using it) to be a descendant of a <BlogPostProvider>.
  2. If you need blog-post data in a component used in multiple contexts, thread the data via props instead of the hook, or conditionally render only on blog post routes.
  3. Wrap a test/storybook render in <BlogPostProvider content={...}> so the context is populated.
  4. Check that a swizzle did not remove the BlogPostProvider wrapper from the post template.

Example fix

// before: component used outside the provider tree
function EditThisPage() {
  const { metadata } = useBlogPost(); // throws if not under BlogPostProvider
}
// after: render it inside the post template that wraps with BlogPostProvider
<BlogPostProvider content={content} isBlogPostPage>
  <EditThisPage />
</BlogPostProvider>
Defensive patterns

Strategy: validation

Validate before calling

import {useContext} from 'react';
import {Context as BlogPostContext} from '@docusaurus/plugin-content-blog/client';

function useOptionalBlogPost() {
  const ctx = useContext(BlogPostContext);
  return ctx; // null => no provider; consumer decides
}

// const post = useOptionalBlogPost(); if (!post) return null;

Type guard

const isInsideBlogPostProvider = (ctx: unknown): ctx is NonNullable<typeof ctx> =>
  ctx !== null;

Try / catch

// Wrap the subtree in an ErrorBoundary that renders a fallback
// if a component is rendered outside BlogPostProvider by mistake.
class SafeBoundary extends React.Component<{}, {hasError: boolean}> {
  state = {hasError: false};
  static getDerivedStateFromError() { return {hasError: true}; }
  render() { return this.state.hasError ? null : this.props.children; }
}

Prevention

When it happens

Trigger: Calling useBlogPost() in a navbar/footer/layout component that is not under BlogPostProvider; swizzling a low-level component and using it on a non-blog-post page; rendering a blog-post-only subcomponent inside a docs or page layout.

Common situations: Swizzling 'BlogPostItem' internals and reusing them in a list header that lacks the provider; an ErrorBoundary swallowing the provider during a render error so children re-render without context; importing a blog-post component into a custom MDX page.

Related errors


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