There is very good example of how to make a breadcrumbs on site in examples folder of react-router repo. But I'm wondering how to make breadcrumbs with dynamic routes.
Assume we have this configuration:
ReactDOM.render((
<Router history={browserHistory}>
<Route path="/projects" component={ProjectsApp}>
<IndexRoute component={ProjectsDashboard} />
<Route path=":projectId" component={ProjectBoard}>
<Route path=":taskId" component={ProjectTaskBoard}>
</Route>
</Route>
</Router>
), document.getElementById('app'));
I want to make something like this:
Projects(link to '/projects') -> MyAwesomeProject (link to '/projects/11'. Title taken from somewhere (from store by id from routeParams or by another way)) -> MyTask (link to '/projects/11/999')
How can I achieve this result? Are there any best practices? Thanks.
Dynamic breadcrumb componentsIf you pass a component as the breadcrumb prop it will be injected with react-router's match and location objects as props. These objects contain ids, hashes, queries, etc... from the route that will allow you to map back to whatever you want to display in the breadcrumb.
Breadcrumbs dynamic navigation is implemented using various JavaScript functions like jQuery prepend(),clone() and click() methods. On click of each navigation link <a> , the child nodes are appended to its parent along with the “GeeksforGeeks /” link in the bottom div with the class display.
Dynamic Routing First, grab yourself a Router component for the environment you're targeting and render it at the top of your app. // react-native import { NativeRouter } from "react-router-native"; // react-dom (what we'll use here) import { BrowserRouter } from "react-router-dom"; ReactDOM.
We use the package react-breadcrumbs-dynamic which is most
flexible for any router (included react-router v4).
import {
BreadcrumbsProvider,
Breadcrumbs,
BreadcrumbsItem
} from 'react-breadcrumbs-dynamic'
const theApp = (
<BreadcrumbsProvider>
<App />
</BreadcrumbsProvider>
)
const App = (props) => (
return (
<div className="App">
<Breadcrumbs/>
{props.children}
</div>
)
}
const Page = (props) => (
<div>
<BreadcrumbsItem to='/'>Main Page</BreadcrumbsItem>
{props.children}
<Route exact path="/user" component={User} />
</div>
)
const User = (props) => (
<div>
<BreadcrumbsItem to='/user'>Home</BreadcrumbsItem>
<h2>Home</h2>
</div>
)
The repo is here: react-breadcrumbs-dynamic
There are several ways to do this, and several open-source solutions (see answers here as well: How do I create react-router v4 breadcrumbs?)
Personally, I prefer a HOC solution because of the small surface area, rendering flexibility, and readable breadcrumb route config.
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}>
{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
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.
With react-router v4, all routes are dynamic (although I think there are still options to write static definitions).
As mentioned by @tbo in there comment, react-breadcrumbs was an ok solution for implementing this with version 3 of react-router. There were even some workarounds for the dynamic breadcrumbs you describe (e.g. using the prepend
or getDisplayName
props).
There is a conversation going on in this issue to determine how to best support react-router v4 which definitely ties into your question as well. I've dug through a bunch of other breadcrumbs component repositories which all seem to struggle from with the same static route issues, some having similar issues open for figuring out v4 support.
TLDR; If you're still on react-router v3, you can probably make the react-breadcrumbs package work for dynamic breadcrumbs. If you've migrated to v4, stay tuned and I'll hopefully have a better answer soon.
After finding no other solutions that fit my needs I developed a solution and opened a pull request on the react-breadcrumbs repository. Auto-breadcrumb, as mentioned below, and this discussion are also potential solutions but allow less customization. If you need more customization, subscribe to and feel free to comment on or help with the pr.
That pull request was merged, and react-breadcrumbs now works great for react-router
version 4 and up.
Auto-Breadcrumb seems to be the best option if you're on version 4 of React Router. I've yet to find a great tutorial but there is an uncommented, but fairly simple demo available.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With