{"record":{"id":"c9895575a687f5b8","repo":"spree/spree","slug":"usesidebar-must-be-used-within-a-sidebarprovider","errorCode":null,"errorMessage":"useSidebar must be used within a SidebarProvider.","messagePattern":"useSidebar must be used within a SidebarProvider\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/dashboard-ui/src/ui/sidebar.tsx","lineNumber":45,"sourceCode":"  /**\n   * Sets the state without remembering it. For collapses the app imposes on\n   * the user's behalf (e.g. the settings area folding the nav to icons to make\n   * room for its own): the merchant's own choice, made through the trigger,\n   * must survive it.\n   */\n  setOpenTransient: (open: boolean) => void\n  openMobile: boolean\n  setOpenMobile: (open: boolean) => void\n  isMobile: boolean\n  toggleSidebar: () => void\n}\n\nconst SidebarContext = React.createContext<SidebarContextProps | null>(null)\n\nfunction useSidebar() {\n  const context = React.useContext(SidebarContext)\n  if (!context) {\n    throw new Error('useSidebar must be used within a SidebarProvider.')\n  }\n\n  return context\n}\n\nfunction SidebarProvider({\n  defaultOpen = true,\n  open: openProp,\n  onOpenChange: setOpenProp,\n  className,\n  style,\n  children,\n  ...props\n}: React.ComponentProps<'div'> & {\n  defaultOpen?: boolean\n  open?: boolean\n  onOpenChange?: (open: boolean) => void\n}) {","sourceCodeStart":27,"sourceCodeEnd":63,"githubUrl":"https://github.com/spree/spree/blob/06bf66a8684b9de03210bbb2ddc8c1f5ba522fa2/packages/dashboard-ui/src/ui/sidebar.tsx#L27-L63","documentation":"useSidebar() reads SidebarContext, created only by <SidebarProvider> in @spree/dashboard-ui. The provider owns desktop open state, mobile open state, and isMobile detection; every sidebar compound component (trigger, menu, inset) consumes it. Calling the hook outside the provider throws.","triggerScenarios":"Rendering <SidebarTrigger> (or any Sidebar* compound component / custom component calling useSidebar()) outside <SidebarProvider> — e.g. a collapse button placed in a header rendered before the provider, a sidebar widget reused on a non-sidebar page, or a test render without the wrapper.","commonSituations":"Shadcn-style sidebar adoption in a custom host app: developers copy <Sidebar> markup but skip the provider; unit tests for menu components; refactoring the app shell so the sidebar renders on a route that no longer sits under the provider.","solutions":["Wrap the app layout (the part containing the sidebar and its trigger) in <SidebarProvider>","In tests, render(<SidebarTrigger />, { wrapper: SidebarProvider })","Keep the trigger inside the provider tree — if the header lives outside, lift the provider up to their common parent","For pages without a sidebar that still need a collapse button, drive your own state instead of useSidebar()"],"exampleFix":"// before\n<header><SidebarTrigger /></header> {/* outside the provider */}\n<SidebarProvider>\n  <Sidebar>...</Sidebar>\n</SidebarProvider>\n\n// after — provider wraps both sidebar and trigger\n<SidebarProvider>\n  <AppShell>\n    <Sidebar>...</Sidebar>\n    <header><SidebarTrigger /></header>\n  </AppShell>\n</SidebarProvider>","handlingStrategy":"validation","validationCode":"// SidebarProvider must wrap both <Sidebar> and every <SidebarTrigger> — put it\n// around the whole app layout:\n<SidebarProvider>\n  <AppShell>{/* Sidebar + header with SidebarTrigger */}</AppShell>\n</SidebarProvider>","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Copy the provider when copying shadcn sidebar markup — the components are unusable without it","Put the provider at the layout level so any route using the sidebar or its trigger is covered","Wrap sidebar component tests with SidebarProvider"],"tags":["react","typescript","react-context","dashboard-ui","sidebar","compound-component","shadcn"],"backgroundTag":"react-context-provider-missing","analyzedSha":"06bf66a8684b9de03210bbb2ddc8c1f5ba522fa2","analyzedAt":"2026-08-21T14:59:48.125Z","schemaVersion":2},"datasetVersion":"2026-08-21T18:17:14.833Z"}