How do I create react-router v4 breadcrumbs? I tried asking this question on the react-router V4 website via an issue ticket. They just said to see the recursive paths example. I really want to create it in semantic-ui-react
7 Answers
I was after the same thing and your question pointed me in the right direction.
This worked for me:
const Breadcrumbs = (props) => (
<div className="breadcrumbs">
<ul className='container'>
<Route path='/:path' component={BreadcrumbsItem} />
</ul>
</div>
)
const BreadcrumbsItem = ({ match, ...rest }) => (
<React.Fragment>
<li className={match.isExact ? 'breadcrumb-active' : undefined}>
<Link to={match.url || ''}>
{match.url}
</Link>
</li>
<Route path={`${match.url}/:path`} component={BreadcrumbsItem} />
</React.Fragment>
)
![](../../users/profiles/6942319.webp)
- 748
- 5
- 14
![](../../users/profiles/7705335.webp)
- 291
- 2
- 3
-
Excellent solution, but `span` cannot be a child of `ul`. Can we check if there are matching paths exists or not before rending `li` ? – Jashwant Feb 22 '20 at 18:37
-
2Instead of ``, I used `
`. – Jashwant Feb 22 '20 at 19:01 -
1@Jashwant I think `React.Fragment` was released after I posted this answer, but sure is a better approach today. Can you suggest an edit on the answer? – Felipe Taboada Feb 27 '20 at 17:42
-
@Jashwant About the check before rendering `li`, `react-router` only renders routes that match. If for some reason you still want to check something before returning the `li`, I guess you could do a conditional or something like that. – Felipe Taboada Feb 27 '20 at 17:46
-
Super elegant solution. Thank you a lot! – pinguinjkeke May 11 '20 at 17:05
-
this becomes a problem when you have ID pages, like /students/:id it shows students twice – Jean Michell Nov 04 '20 at 13:13
I used semantic-ui-react
for my own project and did this to create breadcrumbs based on location.pathname
;
export default (props) => {
const paths = props.pathname.split('/').map((p, i, arr) => {
if (i === 0) return {
key: i,
content: (<Link to={'/'}>home</Link>),
active: (i === arr.length - 1),
link: (i < arr.length - 1)
};
if (i === arr.length - 1) return {
key: i,
content: p,
active: (i === arr.length - 1)
};
return {
key: i,
content: (<Link to={`${arr.slice(0, i + 1).join('/')}`}>{p}</Link>),
active: (i === arr.length - 1),
link: (i < arr.length - 1)}
};
);
return <Breadcrumb icon='chevron right' sections={paths}/>;
};
![](../../users/profiles/1732711.webp)
- 159
- 2
- 6
The problem with both of these approaches is that you're limited to using the path name in the breadcrumb trail; that is, you have to tie your routing to the presentation names of your trail.
![](../../users/profiles/1864795.webp)
- 1,514
- 12
- 16
This can be done using a HOC that parses the pathname
from react-router and returns matches against it. While a little more verbose, I think it gives greater flexibility and a nice readable breadcrumb config object array.
Breadcrumbs.jsx
import React from 'react';
import { NavLink } from 'react-router-dom';
import { withBreadcrumbs } from 'withBreadcrumbs';
const UserBreadcrumb = ({ match }) =>
<span>{match.params.userId}</span>; // use match param userId to fetch/display user name
const routes = [
{ path: 'users', breadcrumb: 'Users' },
{ path: 'users/:userId', breadcrumb: UserBreadcrumb},
{ path: 'something-else', breadcrumb: ':)' },
];
const Breadcrumbs = ({ breadcrumbs }) => (
<div>
{breadcrumbs.map(({ breadcrumb, path, match }) => (
<span key={path}>
<NavLink to={match.url}> // wrap breadcrumb with semantic-ui element
{breadcrumb}
</NavLink>
<span>/</span>
</span>
))}
</div>
);
export default withBreadcrumbs(routes)(Breadcrumbs);
withBreadcrumbs.js
import React from 'react';
import { matchPath, withRouter } from 'react-router';
const renderer = ({ breadcrumb, match }) => {
if (typeof breadcrumb === 'function') { return breadcrumb({ match }); }
return breadcrumb;
};
export const getBreadcrumbs = ({ routes, pathname }) => {
const matches = [];
pathname
.replace(/\/$/, '')
.split('/')
.reduce((previous, current) => {
const pathSection = `${previous}/${current}`;
let breadcrumbMatch;
routes.some(({ breadcrumb, path }) => {
const match = matchPath(pathSection, { exact: true, path });
if (match) {
breadcrumbMatch = {
breadcrumb: renderer({ breadcrumb, match }),
path,
match,
};
return true;
}
return false;
});
if (breadcrumbMatch) {
matches.push(breadcrumbMatch);
}
return pathSection;
});
return matches;
};
export const withBreadcrumbs = routes => Component => withRouter(props => (
<Component
{...props}
breadcrumbs={
getBreadcrumbs({
pathname: props.location.pathname,
routes,
})
}
/>
));
Open-source HOC is also available here: https://github.com/icd2k3/react-router-breadcrumbs-hoc
![](../../users/profiles/1411364.webp)
- 131
- 2
- 6
-
2While this link may answer the question, it is better to include the essential parts of the answer here and provide the link for reference. Link-only answers can become invalid if the linked page changes. - [From Review](/review/low-quality-posts/17819984) – Jagjot Nov 02 '17 at 18:22
-
Fair enough @JagjotSingh! I edited my answer to include all relevant source code. – Justin Schrader Nov 03 '17 at 20:46
Try this simple solution:
const Breadcrumbs = ({ ...rest, match }) => (
<span>
<Link to={match.url || ''} className={match.isExact ? 'breadcrumb active' : 'breadcrumb'}>
{match.url.substr(match.url.lastIndexOf('/')+1, match.url.length)}
</Link>
<Route path={`${match.url}/:path`} component={Breadcrumbs} />
</span>
)
Your css:
.breadcrumbs {
background: #fff;
margin-bottom: 15px;
}
.breadcrumb {
margin-bottom: 0;
line-height: 2.5;
display: inline-block;
}
.breadcrumb::before {
display: inline-block;
padding-right: 5px;
padding-left: 5px;
color: #818a91;
content: "/"; }
.breadcrumb.active {
color: #818a91; }
Then use it like this:
<div className="container-fluid breadcrumbs">
<Route path='/:path' component={Breadcrumbs} />
</div>
Happy coding!
![](../../users/profiles/1619619.webp)
- 3,894
- 5
- 44
- 55
-
How do I remove the first / on the list? I have tried css for not the first child but that removes it of all of them – Andrew Apr 29 '19 at 08:00
-
From react-router
doc: the <Route>
componens is rendering some of your components when a location matches the route’s path like this:
<Route path={`${match.url}/:topicId`} component={Topic}/>
Basic responsibility to the information is available to rendered componet which is <Topic>
in this case. It know how to fetch data or it already have tied redux state and so on. So <Topic>
simple instantiate breadcrums item agent and pass required data to it like this:
import {BreadcrumbsItem} from 'react-breadcrumbs-dynamic'
const Topic = ({ match, topic }) => (
<div>
<h3>
{topic.title}
</h3>
<BreadcrumbsItem to={`${match.url}/${match.params.topicId}`}>
{topic.title}
</BreadcrumbsItem>
...
</div>
)
That's all. More full example in this answer. Here live demo.
![](../../users/profiles/7694876.webp)
- 344
- 1
- 13
Here is the solution providing single source of truth for nested navigation and breadcrumbs.
The example app is available on GitHub: https://github.com/sneas/react-nested-routes-example
Demo: https://sneas.github.io/react-nested-routes-example/
Navigation configuration:
export const navigation = [
{
path: "/",
label: "All categories",
content: () => <AllCategories />,
routes: [
{
path: "/electronics",
label: "Electronics",
content: () => <Electronics />,
routes: [
{
path: "/accessories",
label: "Accessories",
content: () => <Accessories />,
routes: [
{
path: "/usb-cables",
label: "USB cables",
content: () => <UsbCables />
}
]
},
{
path: "/headphones",
label: "Headphones",
content: () => <Headphones />
}
]
}
]
}
];
We have to recursively flatten navigation and render it a flat array:
const routes = flattenRoutes(navigation);
return (<Router>
{routes.map((route, index) => (
<Route
key={index}
path={route.path}
render={() => rouete.content}
></Route>
))}
</Router>);
Then build breadcrumbs out of the same navigation structure.
![](../../users/profiles/379949.webp)
- 728
- 3
- 20