First of all, I would like to thank the creators of Polaris for a great UI kit and detailed documentation. It already saved me a lot of time during a design process and implementation! 馃憤
The Page component has support for breadcrumbs, however, each breadcrumb is a <a /> element. Right now, it's only possible to pass the url option to define a href attribute for a breadcrumb. While this is convenient for most of the apps, some applications have their own navigation, which requires programmatic support for links.

Take for example react-router. It forces you to use their own link components (Link or NavLink), or for more advanced cases history object in a click handler.
It's natural to expect some kind of onClick property in a breadcrumb declaration:
<Page breadcrumbs={[
{ content: "Back to Pages", onClick: () => { ... }}
]}
/>
If you take look at the PageList component you'll notice that it actually has support for click handlers through the onAction property. Maybe it would make sense to implement the same for breadcrumbs? I'm open to a discussion.
Right now it's only possible to pass the url prop to a breadcrumb:
<Page breadcrumbs={[
// works fine for html pages, but not for SPAs :(
{ content: "Back to Pages", url: "/home/pages" }
]}
/>
Page component with breadcrumbs.Thanks for the issue, @molefrog. We actually have an undocumented but fully supported way of swapping out the component we use for all link tags, which we use to support our own use of React Router. In its simplest form, you would do something like this before you render anything in your app:
import {useLinkComponent} from '@shopify/polaris';
import {Link} from 'react-router';
useLinkComponent(Link);
This will also apply to all other places we generate links for you (typically, anything that lets you pass in an action prop of some kind with a url property). It also lets you provide any custom logic you would like for how to handle those links (we actually do this to change the behaviour of links going to non-React pages versus those going to React pages in our app).
Let me know if this isn't clear or if it doesn't solve your problem. I'll add a note for us to add this documentation in a coming release.
@lemonmade Thanks for a quick response!
The solution makes sense, I'll definitely try it out.
@molefrog did you manage to get this working in the end? @lemonmade I'm curious - does this support the latest version of react-router-dom?
I may be implementing it completely wrong; however, when I setup useLinkComponent and update, my code from:
<Page
title="Dashboard"
breadcrumbs={breadcrumbs}
primaryAction={{content: 'New Promotion', link: "/promotion/add"}}
>
to
<Page
title="Dashboard"
breadcrumbs={breadcrumbs}
primaryAction={{content: 'New Promotion', to: "/promotion/add"}}
>
The primaryAction is simply updated to a button; which makes sense as I can see the implementation has a ternary condition here:
which will used an unstyled link only if "link" is passed. I obviously initially attempted to bypass this by passing both "link" and "to" but as you can see in the same file, the proxied attributes are hardcoded.
Maybe I'm overcomplicating this? Or it may be that it's not implemented for this edge case?
Either way thank you for the awesome components!
Kieran
I think you want to use url instead of link/ to. We eventually normalize it to to so that React Router's Link components can handle it. On a separate note, is this part of an embedded app? If so, the actions/ breadcrumbs get rendered in the admin, not in your iframe, so they will not use any custom link you give them.
Hey @lemonmade - thank you for the incredibly fast response.
Sorry, my mistake - I did actually use "url", just a typo in my comment. In this case though I get an error complaining that the "to" attribute is required by <Link>, naturally as react-router-dom's Link component requires the "to" attribute.
Yeah - it is part of an embedded app, so ultimately it's going to reload the iframe every page change anyway isn't it?
Thank you
Kieran
Can you post the code where you are getting the error? We should eventually be translating url -> to when giving it to the component, but there may be cases where we are not.
Yes, if this is an embedded app, we will just be changing the location on the iframe directly. For actions, you can switch to use onAction and manually push onto the router:
<Page primaryAction={{content: 'My action', onAction: () => this.props.router.push('/new/page')}} />
Unfortunately, we do not support the same for breadcrumbs. We are aware of this and have added it as an issue for us to look at internally.
Hi @lemonmade ,
I've tried this but unfortunately this does not seem to work with React Router 4. Are there any workaroudn to enable SPA links with Router 4? I've tried to use the new this.props.history.push('/settings') instead but it does not seem to work :(.
What I've tried so far (without any luck).
1) First, I have the different routes defined in my App component:
class App extends React.Component {
render() {
let content = null;
if (this.props.isAuthenticated) {
content = <Switch>
<Route exact path="/" component={Home} />
<Route path="/settings" component={Settings} />
</Switch>;
} else {
content = <p>Is loading...</p>;
}
return (
<EmbeddedApp
apiKey={apiKey}
forceRedirect={forceRedirect}
shopOrigin={shopOrigin}
>
{content}
</EmbeddedApp>
);
};
}
Then in my Home component (which hold an action link to a settings page), I have this:
import * as React from 'react';
import {Redirect} from 'react-router-dom';
import {Layout, Page} from '@shopify/polaris';
export default class Home extends React.Component {
constructor(props) {
super(props);
this.state = {redirectToSettings: false};
}
render() {
if (this.state.redirectToSettings) {
return <Redirect to="/settings" />;
}
return <Page
fullWidth
secondaryActions={[
{content: 'Settings', onAction: () => this.setState({redirectToSettings: true})}
]}
>
<Layout>
</Layout>
</Page>;
}
}
The idea is that on click, it re-renders to use a Redirect. Unfortuantely it does not seem to work. Interestingly, if I redirect from the start:
import * as React from 'react';
import {Redirect} from 'react-router-dom';
import {Layout, Page} from '@shopify/polaris';
export default class Home extends React.Component {
render() {
return <Redirect to="/settings" />;
}
}
This one works, but not after the click. Anyone have an idea? :)
@bakura10 I just tried to reproduce your example using <Page> component and react-router v4 and it did work well for me.
I think the issue you have described might not be related to Polaris, by to the way use you react-router. Try adding a simple <button> element and do the redirect on click. Chances are it won't again, which will mean that it's not a bug in Polaris.
Thanks @molefrog . I will try this tomorrow and let you know!
There must be something I miss in the doc. I've tried to add a simple button in the "App" component (where I define my route), and here it works perfectly with this:
<button onClick={() => this.props.history.push('/settings')}>Test</button>
However if I have this in my "Home" component (so a route component) or any other nested component, then it does not work... I'll have to dig more the doc but I don't see why it does not work.
Okay, found the issue ! In case someone run into the trouble again, what you need to do is simply make sure that you wrap the App component (the top one taht define your routes) with the "withRouter" helper of React Router app. I was quite surprised I needed to do that is the router props is already part of this one, but well... I'm glad to have found it finally :).
Great work guys and thanks for the issue - super helpful! Is this also working for the <ResourceList> component? I'm always getting an error ' requires 'to' property'.
@m20io can you open a separate issue for this? We will probably need some more detail on the error to diagnose.
Is there a working example of the following code somewhere?
import {useLinkComponent} from '@shopify/polaris';
import {Link} from 'react-router';
useLinkComponent(Link);
I'm having a difficult time getting this to work inside of a url property inside the actions prop to work with React Router V4.
@tberry30 See my comment here with what I did to get it working with React Router V4.
'useLinkComponent' was not found in '@shopify/polaris' is there any alternative to be able to work as SPA ?
Most helpful comment
@tberry30 See my comment here with what I did to get it working with React Router V4.