Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Dynamic breadcrumbs using react router

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.

like image 973
Yaroslav Melnichuk Avatar asked Feb 01 '16 08:02

Yaroslav Melnichuk


People also ask

How do you use breadcrumbs on a router react?

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.

How do you make dynamic breadcrumbs?

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.

How use dynamic router in react JS?

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.


5 Answers

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

like image 119
Alex Avatar answered Oct 04 '22 19:10

Alex


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

like image 32
Justin Schrader Avatar answered Oct 04 '22 19:10

Justin Schrader


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.

like image 42
sneas Avatar answered Oct 04 '22 19:10

sneas


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.

Update

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.

Update (2020)

That pull request was merged, and react-breadcrumbs now works great for react-router version 4 and up.

like image 21
Greg Venech Avatar answered Oct 04 '22 19:10

Greg Venech


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.

like image 44
newswim Avatar answered Oct 04 '22 19:10

newswim