When dashboard extensions dynamically import hooks from @vendure/dashboard, Vite can create duplicate module instances. If a React Context is defined in the same file as a hook that consumes it, the extension's hook will reference a different Context object than the one used by the main app's Provider - causing "must be used within a Provider" errors even when the component IS inside the provider.
Never define a React Context and its consuming hook in the same file within src/lib/.
Split them into separate files:
createContext() and the Provider componentsrc/lib/hooks/, imports context via @/vdb/ path// ❌ BAD: Context and hook in same file (src/lib/components/my-context.tsx)
export const MyContext = createContext<MyContextValue | undefined>(undefined);
export function MyProvider({ children }) {
return <MyContext.Provider value={...}>{children}</MyContext.Provider>;
}
export function useMyContext() {
const context = useContext(MyContext); // Same file = module identity issues
if (!context) throw new Error('...');
return context;
}
// ✅ GOOD: Context in one file, hook in separate file
// src/lib/components/my-context.tsx
export const MyContext = createContext<MyContextValue | undefined>(undefined);
export function MyProvider({ children }) {
return <MyContext.Provider value={...}>{children}</MyContext.Provider>;
}
// src/lib/hooks/use-my-context.ts
import { MyContext } from '@/vdb/components/my-context.js';
export function useMyContext() {
const context = useContext(MyContext);
if (!context) throw new Error('...');
return context;
}
Both the main app and dynamically-imported extension code resolve the context import to the same module instance when using internal @/vdb/ paths, preserving React Context identity.
This pattern is enforced by scripts/check-lib-imports.js, which runs on pre-commit. It will fail if any file in src/lib/ contains both createContext( and useContext(.
Allowlist: Some shadcn UI primitives (carousel, chart, form, toggle-group) are allowlisted because their contexts are internal implementation details not accessed by extensions.
All hooks in src/lib/hooks/ must:
@/vdb/ prefix (not relative ../ paths)@/vdb/index.js directlyThese rules are also enforced by the same lint script.