Answer
The route renders because the authentication guard is not attached to the route definition, so React Router treats it as a normal route and renders the component directly.
Likely explanation vs. confirmed facts
Confirmed fact: In React Router v6 a route is protected only when the element prop (or the children of a Route) includes logic that checks authentication and either redirects or returns null. If that logic is omitted, the router has no interception point and will mount the component regardless of auth state.
Likely explanation: You likely defined the route as { path: '/dashboard', element: } without wrapping Dashboard in a guard component. Consequently, the router renders Dashboard before any auth check can run.
Steps to fix this case
- Verify the route configuration – ensure the route uses a wrapper that performs the auth check.
- Add a guard wrapper – create a component like
RequireAuth that reads your auth state (context, cookie, or store) and either redirects to /login or renders its children.
- Position the guard relative to lazy loading – place the guard outside
React.lazy and Suspense so the lazy component is only fetched after the guard passes.
- Handle SSR (if applicable) – run the same auth check on the server (e.g., in
getServerSideProps or a middleware) and redirect with a 302 before sending HTML, or hydrate the client with the same auth state to avoid a flash of unauthenticated UI.
Example guard wrapper
import { Navigate, useLocation } from 'react-router-dom';
import { useAuth } from './authContext'; // your auth hook
export function RequireAuth({ children }) {
const { isAuthenticated } = useAuth();
const location = useLocation();
return isAuthenticated ? children : ;
}
Usage with lazy loading
const Dashboard = React.lazy(() => import('./pages/Dashboard'));
}>
}
/>
If you are rendering on the server, move the auth check before rendering the route tree (e.g., in Next.js getServerSideProps) and redirect there; otherwise the client‑side guard works as shown.