I'm building a reusable dashboard package that I publish to npmjs. Consumers can provide dynamic menu items and top bar configuration via props, like this:
<Dashboard
menuItems={[
{
label: 'Home',
icon: 'home',
action: ({ navigate }) => navigate('/')
}
]}
topBarConfig={...}
/>
To enable navigation in these actions, I use useNavigate() inside my dashboard's internal LeftMenu component:
import { useNavigate } from 'react-router-dom';
const LeftMenu = ({ menuItems }) => {
const navigate = useNavigate(); // {
item.action({ navigate, closeMenu: () => {/* close logic */} });
};
...
};
This works perfectly fine when used locally (i.e. consuming the dashboard component directly inside the same project), but when I install it from npm, and use it like this:
<pre><code>
import AuthProvider, { useAuth } from "./Providers/AuthProvider";
import Login from './Login';
import ProtectedRoutes from "./ProtectedRoute";
import { BrowserRouter as Router, Routes, Route, Navigate } from "react-router-dom";
import { Dashboard, ThemeProvider, SearchWidget, UserDropdown } from 'leximo-dashboard';
import 'leximo-dashboard/dist/assets/style.css';
export default function App() {
return (
<AuthProvider>
<ThemeProvider>
<Router>
<AppContent />
</Router>
</ThemeProvider>
</AuthProvider>
);
}
function AppContent() {
const { user } = useAuth();
const menuItems = [
{
label: 'Home',
icon: 'home',
action: ({ navigate }) => navigate('/')
}
];
const topBarConfig = {
leftContent: null,
centerContent: <SearchWidget placeholder="Find anything..." onSearch={(term) => console.log(term)} />,
rightContent: (
<UserDropdown
items={[
{ label: 'Profile', icon: 'user' },
{ label: 'Notifications', icon: 'bell' },
{ label: 'Logout', icon: 'sign-out-alt' }
]}
/>
),
showThemeToggle: true
};
return (
<Routes>
<Route element={<ProtectedRoutes />}>
<Route path="/" element={<Dashboard menuItems={menuItems} topBarConfig={topBarConfig} />} />
</Route>
<Route path="/login" element={<Login />} />
<Route path="*" element={<Navigate to="/" replace />} />
</Routes>
);
}
then, I get the following runtime error:
Uncaught Error: useNavigate() may be used only in the context of a <Router> component
The error means that leximo-dashboard uses react-router-dom copy that differs from the one that's used in the project.
There can be several causes for this. The actual problem is that leximo-dashboard bundles react-router-dom package, this can be easily seen by looking at the size of the bundle. This should be avoided, if the package is bundled with Vite, react* packages should be added to build.rollupOptions.external.
Additional things to consider:
react-router-dom should be peer dep in the package (it already is)
Make sure react-router-dom has a loose version constraint to not cause problems for a user (it doesn't yet)
leximo-dashboard should be installed from npm and not with a symlink to not cause dupe react-router-dom package in node modules hierarchy
Make sure there's no node_modules/leximo-dashboard/node_modules/react-router-dom. If the problem persists, run npm dedupe