This is a Breadcrumb component for react, redux, and react-router v3 that supports a stack-based trail that follows your actual navigation through the application. Ordinarily breadcrumb components work by tracing your static
<Routes>
layout. If your app contains a lot of side-navigation between un-nested pages, a dynamic stack-based breadcrumb can be useful to you.
This is how it works:
- Normal site navigation using
<Link>
orrouter.push('/about')
appends the new page to the breadcrumb trail - Visiting a page that is already part of the breadcrumb will "pop back" to that position in the stack
- If you visit the same route but the params are different (e.g.
<Route path="/product/:id" />
for two different IDs) that would be two different pages - You can reset the breadcrumb for top-level navigation events by replacing
<Link>
with<NavLink>
fromredux-breadcrumb-trail
or by callingrouter.push({ pathname: '/about', state: { breadcrumb: 'reset' } })
This package is intended to be installed along with react-router v3, redux, and react-redux. So be sure you have those prerequisites installed as well.
npm install --save redux-breadcrumb-trail react-router@3.0.2 redux react-redux
The goal of this package was to be able to be plugged into a pre-existing React application with minimal effort. You should be able to reuse all of your existing components and routes. If you are starting fresh, first set-up a working site with react-router v3 and react-redux, or use the example site in this repo as a guideline.
In your main App component or wherever you want the breadcrumb to appear, render <Breadcrumbs routes={routes} params={params} location={location} />
where routes, params, and location are all the props from react-router
with the same names.
import React from 'react'
import Breadcrumbs, { NavLink } from 'redux-breadcrumb-trail'
export default function App (props) {
const {children, routes, params, location} = props
return (
<div>
<ul>
<li><NavLink to='/products'>Products</NavLink></li>
<li><NavLink to='/locations'>Locations</NavLink></li>
</ul>
<Breadcrumbs
className='list-inline'
routes={routes}
params={params}
location={location}
/>
{children}
</div>
)
}
The <Breadcrumbs />
component supports the following props:
prop | type | description |
---|---|---|
itemRenderer | function or react node | The container for each breadcrumb item. Default is 'li' |
listRenderer | function or react node | The container for the breadcrumb control. Default is 'ul' |
separatorRenderer | function or react node | The element to render in between each breadcrumb item. Default is () => <li>></li> |
Use the Renderer props to define a custom breadcrumb component that fits with your application's style.
Add the reducer to your redux store:
import { createStore, combineReducers } from 'redux'
import { reducer as breadcrumb } from 'redux-breadcrumb-trail'
const rootReducer = combineReducers({ breadcrumb })
export default createStore(rootReducer)
In your <Router>
, there are a number of new options for <Route>
components:
prop | type | description |
---|---|---|
breadcrumb | any valid React node, component cache entry, or a function | The label that appears in the breadcrumb component when the route is part of it. All of your routes should contain either "breadcrumb" or "useParentBreadcrumb" |
breadcrumbKey | string | Routes that share a breadcrumbKey will automatically "pop back" on each other. Defaults to "path" |
useParentBreadcrumb | bool | This route is kept out of the breadcrumb and its parent is updated instead. Useful for tabbed interfaces or other nested route structures where you don't want the child routes to be part of the breadcrumb |
ignoreParams | bool | Ignores params when matching the route to previous breadcrumbs. With this prop set to true , "//products//:id" would consider "//products//1" and "//products//2" to be a match. Defaults to false |
<Router history={history}>
<Route path='/' component={App}>
{/* The breadcrumb can be any React node */}
<IndexRoute component={Home} breadcrumb={<i className='fa fa-home' />} />
{/* Components can also be used and will receive the route params as props */}
<Route path='products' component={Products} breadcrumb='Products' />
{/* Example of a tabbed interface with child routes ignored */}
<Route path='products/:id' component={Product} breadcrumb={ProductBreadcrumb}>
<IndexRedirect to='summary' />
<Route path='summary' component={SummaryTab} useParentBreadcrumb />
<Route path='detail' component={DetailTab} useParentBreadcrumb />
</Route>
<Route path='locations' component={Locations} breadcrumb='Locations' />
<Route path='locations/:id' component={Location} breadcrumb={LocationBreadcrumb} />
</Route>
</Router>
Static breadcrumb text can only get you so far. Eventually, you are going to want to populate a breadcrumb with dynamic text that you have retrieved from your redux store or from some other module. It would be nice to be able to reuse your redux connect(mapStateToProps)
code that you already have in place to render your page if the breadcrumb is going to be accessing the same state. However, breadcrumb components are different from page components because they tend to stick around after the page has been unmounted. And sometimes your redux store has state that gets shared by lots of pages in your app. So how can you connect your custom breadcrumb component using the same connector as your page without all that global state getting corrupted as soon as you visit a neighboring page? Just breadcrumbify
your component after applying the connector.
// LocationBreadcrumb.jsx
import React from 'react'
import { connect } from 'react-redux'
import { breadcrumbify } from 'redux-breadcrumb-trail'
export function LocationBreadcrumb ({location}) {
if (!location.current) return <div><i className='fa fa-refresh fa-spin' /></div>
return <i>{location.current.name}</i>
}
export function mapStateToProps(state) {
return { location: state.location }
}
export default connect(mapStateToProps)(breadcrumbify(LocationBreadcrumb))
breadcrumbify
is a higher-order component that renders like normal while the breadcrumb is active and then "freezes" the props when the breadcrumb becomes inactive. It does so by caching the component's props into the component state.
redux-breadcrumb-trail exports its action creators, giving you direct access to its redux state. This way you can build your own custom breadcrumb controls on top.
actionCreator | description |
---|---|
push({location, params, routes}) | Pushes a new breadcrumb onto the stack. The props should be the corresponding react-router props for the route you want to push. |
reset({breadcrumbs}) | Reset the breadcrumbs to a defined state. The safest way to use this is probably to copy the existing redux state and mutate it to suit your needs. |
After cloning, use npm install
to install dev dependencies. You can run tests with npm test
and build with npm run build
. Launch the example site with cd example
and npm start
.
If you would like to contribute, please follow the coding style by installed standard npm i -g standard
and running standard --fix
before opening your pull request.