I am migrating an app from react-router-dom v5 to v6. The v5 app works by clicking buttons to navigate to a sub page. It changed the page by doing a history.push. Now I am trying the v6 way but it does not work as expected. I even see the desired url in the address bar but the page does not render. I have read that the navigate hook should be called in an event handler which I have in the demo code.
What am I missing?
How should I debug this?
import React from 'react';
import { BrowserRouter as Router, Navigate, Route, Routes, useNavigate } from 'react-router-dom';
function ListOfThings() {
const navigate = useNavigate();
function handleClickCreate( e:React.MouseEvent<HTMLElement>) {
navigate( 'CreateNewThing' );
}
function handleClickShow( e:React.MouseEvent<HTMLElement>) {
navigate( 'ShowThing' );
}
return (
<>
<h2>ListOfThings</h2>
<button onClick={handleClickCreate}>Go To Create New Thing Page</button>
<button onClick={handleClickShow}>Go To Show Thing Page</button>
</>
);
}
function CreateNewThing() {
const navigate = useNavigate();
function handleClick() {
navigate( '/ListOfThings' );
}
return (
<>
<h2>Create New Thing Page</h2>
<button onClick={handleClick}>Go To List Of Things</button>
</>
);
}
function ShowThing() {
const navigate = useNavigate();
function handleClick() {
navigate( '/ListOfThings' );
}
return (
<>
<h2>Show Thing Page</h2>
<button onClick={handleClick}>Go To List Of Things</button>
</>
);
}
function App() {
return (
<Router>
<Routes>
<Route path='/ListOfThings' element={<ListOfThings />} >
<Route path='CreateNewThing' element={<CreateNewThing />} />
<Route path=':id' element={<ShowThing />} />
</Route>
<Route path='/' element={ <Navigate to='ListOfThings' replace /> } />
</Routes>
</Router>
);
}
export default App;
The SubPage
component is rendered by a nested route, the parent layout route component necessarily should render an Outlet
for the nested routes to render their element
content into.
import { Outlet, useNavigate } from 'react-router-dom';
function Home() {
const navigate = useNavigate();
function handleClick() {
navigate('Subpage');
}
return (
<>
<h2>Home</h2>
<button onClick={handleClick}>Go To Sub Page</button>
<div>
<Outlet /> // <-- nested route content rendered here
</div>
</>
);
}
function App() {
return (
<Router>
<Routes>
<Route path='/Home' element={<Home />}>
<Route path='Subpage' element={<SubPage />} /> // <-- rendered into Outlet
</Route>
<Route path='/' element={<Navigate to='Home' replace />} />
</Routes>
</Router>
);
}
For more information see:
If you prefer that the Home
and SubPage
components be rendered individually then move the Home
component off the layout route and render it as an index route.
function App() {
return (
<Router>
<Routes>
<Route path='/Home'>
<Route index element={<Home />} /> // "/home"
<Route path='Subpage' element={<SubPage />} /> // "/home/subpage"
</Route>
<Route path='/' element={<Navigate to='Home' replace />} />
</Routes>
</Router>
);
}