14

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.

Yaroslav Melnichuk
  • 632
  • 1
  • 8
  • 14
  • 1
    I know this is an old question but there is a component for react-router to support this, take a look: https://github.com/svenanders/react-breadcrumbs hope it helps – tbo Sep 29 '16 at 11:55
  • Did you find a solution for this? – Noitidart Nov 22 '16 at 21:05

5 Answers5

9

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

Alex
  • 332
  • 2
  • 12
2

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.

Greg Venech
  • 5,274
  • 2
  • 17
  • 27
1

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.

newswim
  • 382
  • 2
  • 6
1

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

Justin Schrader
  • 131
  • 2
  • 6
1

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.

sneas
  • 728
  • 3
  • 20