Problem
I’m having trouble layering routes in React Router v4 right now.
The route setting in the React-Router v4 Documentation came closest.
I’d like to divide my app into two sections.
There’s a frontend and a backend.
I was considering something along these lines:
<Match pattern="/" component={Frontpage}>
<Match pattern="/home" component={HomePage} />
<Match pattern="/about" component={AboutPage} />
</Match>
<Match pattern="/admin" component={Backend}>
<Match pattern="/home" component={Dashboard} />
<Match pattern="/users" component={UserPage} />
</Match>
<Miss component={NotFoundPage} />
The frontend is designed and styled differently than the admin area. So, within the frontpage, the child routes should be home, about, and so on.
/admin/home should be rendered in the Backend component, whereas /home should be rendered in the Frontpage component.
I tried a few other approaches, but I always ended up missing /home or /admin/home.
Edit – 19.04.2017
Because this post is currently receiving a lot of traffic, I updated it with the ultimate answer. I hope it is useful to someone.
Edit – 08.05.2017
Removed old solutions
Final solution:
This is the final solution that I am currently employing. Like a regular 404 page, this sample also features a global error component.
import React, { Component } from 'react';
import { Switch, Route, Redirect, Link } from 'react-router-dom';
const Home = () => <div><h1>Home</h1></div>;
const User = () => <div><h1>User</h1></div>;
const Error = () => <div><h1>Error</h1></div>
const Frontend = props => {
console.log('Frontend');
return (
<div>
<h2>Frontend</h2>
<p><Link to="/">Root</Link></p>
<p><Link to="/user">User</Link></p>
<p><Link to="/admin">Backend</Link></p>
<p><Link to="/the-route-is-swiggity-swoute">Swiggity swooty</Link></p>
<Switch>
<Route exact path='/' component={Home}/>
<Route path='/user' component={User}/>
<Redirect to={{
state: { error: true }
}} />
</Switch>
<footer>Bottom</footer>
</div>
);
}
const Backend = props => {
console.log('Backend');
return (
<div>
<h2>Backend</h2>
<p><Link to="/admin">Root</Link></p>
<p><Link to="/admin/user">User</Link></p>
<p><Link to="/">Frontend</Link></p>
<p><Link to="/admin/the-route-is-swiggity-swoute">Swiggity swooty</Link></p>
<Switch>
<Route exact path='/admin' component={Home}/>
<Route path='/admin/user' component={User}/>
<Redirect to={{
state: { error: true }
}} />
</Switch>
<footer>Bottom</footer>
</div>
);
}
class GlobalErrorSwitch extends Component {
previousLocation = this.props.location
componentWillUpdate(nextProps) {
const { location } = this.props;
if (nextProps.history.action !== 'POP'
&& (!location.state || !location.state.error)) {
this.previousLocation = this.props.location
};
}
render() {
const { location } = this.props;
const isError = !!(
location.state &&
location.state.error &&
this.previousLocation !== location // not initial render
)
return (
<div>
{
isError
? <Route component={Error} />
: <Switch location={isError ? this.previousLocation : location}>
<Route path="/admin" component={Backend} />
<Route path="/" component={Frontend} />
</Switch>}
</div>
)
}
}
class App extends Component {
render() {
return <Route component={GlobalErrorSwitch} />
}
}
export default App;
Asked by datoml
Solution #1
You don’t nest Routes /> in react-router-v4. Instead, you enclose them in another />Component.
For instance
<Route path='/topics' component={Topics}>
<Route path='/topics/:topicId' component={Topic} />
</Route>
should become
<Route path='/topics' component={Topics} />
with
const Topics = ({ match }) => (
<div>
<h2>Topics</h2>
<Link to={`${match.url}/exampleTopicId`}>
Example topic
</Link>
<Route path={`${match.path}/:topicId`} component={Topic}/>
</div>
)
Here’s a simple example taken directly from the react-router docs.
Answered by Lyubomir
Solution #2
Update for 2022: Version 6 now includes nested Route components that Simply WorkTM.
This is a question about v4/v5, however the best response right now is to utilize v6 if at all possible!
This blog post includes an example code. If you can’t upgrade just yet, however…
It’s true that nesting Routes necessitates placing them in the Route’s child component.
You can pass a functional component to the render prop of the Route you wish to nest under if you want a more inline syntax rather than separating your Routes up among components.
<BrowserRouter>
<Route path="/" component={Frontpage} exact />
<Route path="/home" component={HomePage} />
<Route path="/about" component={AboutPage} />
<Route
path="/admin"
render={({ match: { url } }) => (
<>
<Route path={`${url}/`} component={Backend} exact />
<Route path={`${url}/home`} component={Dashboard} />
<Route path={`${url}/users`} component={UserPage} />
</>
)}
/>
</BrowserRouter>
If you’re wondering why the render prop rather than the component prop should be used, it’s because it prevents the inline functional component from being remounted on each render. More information can be found in the documentation.
The example uses a Fragment to surround the nested Routes. You can use a container div> instead of a div before React 16.
Answered by davnicwil
Solution #3
Just wanted to point out that react-router v4 has undergone significant changes since this topic was asked and answered.
There is no longer a component named Match! The purpose of Switch> is to ensure that only the first match is rendered. Redirect>, on the other hand, redirects to a different route. To include or omit a partial match, use precise or leave it out.
Take a look at the documentation. They’re fantastic. https://reacttraining.com/react-router/
I’ve included an example that I hope will help you answer your issue.
<Router>
<div>
<Redirect exact from='/' to='/front'/>
<Route path="/" render={() => {
return (
<div>
<h2>Home menu</h2>
<Link to="/front">front</Link>
<Link to="/back">back</Link>
</div>
);
}} />
<Route path="/front" render={() => {
return (
<div>
<h2>front menu</h2>
<Link to="/front/help">help</Link>
<Link to="/front/about">about</Link>
</div>
);
}} />
<Route exact path="/front/help" render={() => {
return <h2>front help</h2>;
}} />
<Route exact path="/front/about" render={() => {
return <h2>front about</h2>;
}} />
<Route path="/back" render={() => {
return (
<div>
<h2>back menu</h2>
<Link to="/back/help">help</Link>
<Link to="/back/about">about</Link>
</div>
);
}} />
<Route exact path="/back/help" render={() => {
return <h2>back help</h2>;
}} />
<Route exact path="/back/about" render={() => {
return <h2>back about</h2>;
}} />
</div>
</Router>
Please let me know if this was helpful. If this example does not adequately answer your question, please let me know and I will see what I can do to improve it.
Answered by jar0m1r
Solution #4
I was able to build nested routes by wrapping them with Switch and defining them before the root route.
<BrowserRouter>
<Switch>
<Route path="/staffs/:id/edit" component={StaffEdit} />
<Route path="/staffs/:id" component={StaffShow} />
<Route path="/staffs" component={StaffIndex} />
</Switch>
</BrowserRouter>
Reference: https://github.com/ReactTraining/react-router/blob/master/packages/react-router/docs/api/Switch.md
Answered by asukiaaa
Solution #5
The most recent hook update is to use useRouteMatch.
export default function NestingExample() {
return (
<Router>
<Switch>
<Route path="/topics">
<Topics />
</Route>
</Switch>
</Router>
);
}
function Topics() {
// The `path` lets us build <Route> paths
// while the `url` lets us build relative links.
let { path, url } = useRouteMatch();
return (
<div>
<h2>Topics</h2>
<h5>
<Link to={`${url}/otherpath`}>/topics/otherpath/</Link>
</h5>
<ul>
<li>
<Link to={`${url}/topic1`}>/topics/topic1/</Link>
</li>
<li>
<Link to={`${url}/topic2`}>/topics/topic2</Link>
</li>
</ul>
// You can then use nested routing inside the child itself
<Switch>
<Route exact path={path}>
<h3>Please select a topic.</h3>
</Route>
<Route path={`${path}/:topicId`}>
<Topic />
</Route>
<Route path={`${path}/otherpath`>
<OtherPath/>
</Route>
</Switch>
</div>
);
}
Answered by mohRamadan
Post is based on https://stackoverflow.com/questions/41474134/nested-routes-with-react-router-v4-v5