mastra-ai/mastra · error · Error

useMainSidebar must be used within a MainSidebarProvider.

Error message

useMainSidebar must be used within a MainSidebarProvider.

What it means

useMainSidebar combines MainSidebarContext and MobileDrawerContext, both provided by MainSidebarProvider. If either is missing the hook throws, since sidebar state/consumers depend on both the desktop sidebar state and mobile drawer state being present.

Source

Thrown at packages/playground-ui/src/ds/components/MainSidebar/main-sidebar-context.ts:47

  openMobile: boolean;
  setOpenMobile: (open: boolean) => void;
};

export type MainSidebarStateContextValue = Omit<MainSidebarContextValue, 'openMobile' | 'setOpenMobile'>;

export const MainSidebarContext = React.createContext<MainSidebarStateContextValue | null>(null);
export const MobileDrawerContext = React.createContext<MobileDrawerContextValue | null>(null);

/** Reads sidebar state and actions without subscribing to mobile drawer state. */
export function useMaybeSidebarState(): MainSidebarStateContextValue | null {
  return React.useContext(MainSidebarContext);
}

export function useMainSidebar(): MainSidebarContextValue {
  const ctx = React.useContext(MainSidebarContext);
  const drawer = React.useContext(MobileDrawerContext);
  if (!ctx || !drawer) {
    throw new Error('useMainSidebar must be used within a MainSidebarProvider.');
  }
  return { ...ctx, ...drawer };
}

export function useMaybeSidebar(): MainSidebarContextValue | null {
  const ctx = React.useContext(MainSidebarContext);
  const drawer = React.useContext(MobileDrawerContext);
  if (!ctx || !drawer) return null;
  return { ...ctx, ...drawer };
}

/** Reads only mobile drawer state. Cheap — no re-renders on sidebar resize. */
export function useMobileDrawer(): MobileDrawerContextValue {
  const drawer = React.useContext(MobileDrawerContext);
  if (!drawer) throw new Error('useMobileDrawer must be used within a MainSidebarProvider.');
  return drawer;
}

View on GitHub (pinned to 75dd419e61)

Solutions

  1. Render the consumer inside the layout subtree that contains <MainSidebarProvider>.
  2. If only drawer state is needed, or optional access is acceptable, use useMaybeSidebar (returns null outside a provider) instead.
  3. Move MainSidebarProvider higher in the tree (e.g. root layout) so all consumers are covered.
  4. In tests, wrap the component under test with MainSidebarProvider before rendering.

Example fix

// before
export default function Page() {
  const sidebar = useMainSidebar(); // no provider above
// after
export default function Page() {
  const sidebar = useMaybeSidebar(); // or ensure ancestor <MainSidebarProvider>
Defensive patterns

Strategy: fallback

Validate before calling

const sidebar = useMaybeSidebar();
if (!sidebar) {
  // render a non-sidebar fallback (e.g. plain link) instead of the throwing hook
}

Type guard

function hasSidebar(s: MainSidebarContextValue | null): s is MainSidebarContextValue {
  return s !== null;
}

Try / catch

// Prefer the nullable accessor over catching:
const sidebar = useMaybeSidebar() ?? { state: 'default' /* defaults */ };

Prevention

When it happens

Trigger: Calling useMainSidebar in a component rendered outside <MainSidebarProvider> (or the layout wrapper that mounts it); using it in a layout region (e.g. a top-level page) that sits above the provider in the tree.

Common situations: Adding a sidebar toggle button to a page outside the app shell that hosts the provider; unit tests rendering components without the provider; nesting a new route layout outside the existing shell layout that contains MainSidebarProvider.

Related errors


AI-assisted analysis of mastra-ai/mastra@75dd419e61 (2026-08-30). Data as JSON: /api/errors/b22530131220a0e0. Report an issue: GitHub.