Hacker News new | past | comments | ask | show | jobs | submit login
React Router v5 (reacttraining.com)
156 points by jsdev93 on Mar 21, 2019 | hide | past | web | favorite | 87 comments

Using react router on one of my personal projects (~ 30k loc) was probably one of my largest regrets. At every turn it seemed designed to do the thing I wouldn’t expect, or have arbitrary restrictions that made my life tougher.

Some examples:

* there's no relative routes https://github.com/ReactTraining/react-router/issues/2172

* there's no way to refresh the page https://github.com/ReactTraining/react-router/issues/1982 ("that's your responsibility, not ours")

* The scroll position will stick when you navigate to a new route, causing you to need to create a custom component wrapper to manage scrolling https://github.com/ReactTraining/react-router/issues/3950

* React router's <Link> do not allow you to link outside of the current site https://github.com/ReactTraining/react-router/issues/1147 so you have to make an <a> in those cases. This doesn’t sound bad, but it’s particularly frustrating when dealing with dynamic or user generated content. Why can’t they handle this simple case?

* Navigating to the same page would not actually reload the page, it would just trigger a componentDidMount() on all components in the page again, which led me to have a lot of bugs when I did some initialization in my constructor

I ran into many of the same issues as you, and open-sourced my solution (called Navi). Responding to a few of your pain points:

* Relative links are supported out of the box. If you use '..', it is relative to the current "directory" (i.e. it ignores any trailing slash)

* Navi allows you to reload the current route using `navigation.refresh()`. This is undocumented as I didn't realize that it is a feature people actually need, although I'd be happy to document it in a future release after making sure that the behavior makes sense.

* Navi handles scroll-to-hash and scroll-to-top automatically (and you can disable it by passing a prop)

* The <Link> component works for external links automatically.

* Navigating to the same page does not reload the page, but you could implement this by creating a wrapper around <Link>, and calling `navigation.refresh()` if the link points to the current page.

And while it's not specifically one of your pain points, Navi also works server-side and has an in-built static build tool for SEO – which is probably important given the features that you're looking for.

While it's easy to say "just don't do client-side routing", there are a growing number of sites where client-side routing makes sense. I saw that you're making a music site, and client-side routing definitely makes sense. I'm building content for teaching React/Frontend Dev, and the live editor loads a lot of files that should be cached between pages, so client side routing also makes sense (and besides, it's just so much faster).

Would love to hear any feedback on the tool :-) Here's the URL: https://frontarm.com/navi/en/

> there's no relative routes

> React router's <Link> do not allow you to link outside of the current site

Both valid points. Not show stoppers though. And not enough to make me regret using the library. The solution to the external links issue is a one-liner.

> there's no way to refresh the page https://github.com/ReactTraining/react-router/issues/1982 ("that's your responsibility, not ours")

That is your responsibility. Why do you need the routing library to handle page refreshing for you?

> the scroll position will stick when you navigate to a new route

Making the page scroll to the top when navigating to a new page is trivial. I would 100% rather have this problem instead of the opposite: scroll jumping to the top when I don't want it to. That's so much harder to fix.

> Navigating to the same page would not actually reload the page, it would just trigger a componentDidMount() on all components in the page again, which led me to have a lot of bugs when I did some initialization in my constructor

That's exactly what it's supposed to do. It's a client side routing solution. (I'm also pretty sure that it doesn't remount)

From the issues that you've had with the library, it seems like client side routing is not actually what you're looking for. If you regret using it so much, may I ask what the alternative would be?

IMO a project either needs client side routing, or it doesn't. If it does, then React Router is the obvious choice. Otherwise, of course, don't use it and save yourself from unnecessary complexity.

> The solution to the external links issue is a one-liner.

It is most definitely not a one-liner.

> Making the page scroll to the top when navigating to a new page is trivial.

So do it for me. There’s no reason a routing library should break default behavior.

> That's exactly what it's supposed to do. It's a client side routing solution. (I'm also pretty sure that it doesn't remount)

Then it’s “supposed” to have inconsistent behavior. Navigating anywhere else in my site will call constructors to all my components. Navigating to the same page won’t.

> From the issues that you've had with the library, it seems like client side routing is not actually what you're looking for.

My site was a music site. I wanted clientside routing so I could keep music playing while you navigated between links. Seemed like a slam dunk for clientside routing to me.

> may I ask what the alternative would be?

Quite honestly I would go onto GitHub and look for any routing solution that didn’t have multiple closed unfixed issues with hundreds of thumbs up.

> So do it for me.

I think this will work:

Put this wherever you put your reusable functions:

`const scrollToTop = () => document.getElementById('root').scrollIntoView();`

And put this in the components / functions you want the scrollToTop effect to work on:

`useEffect(() => { scrollToTop() }, []);`

And put this in your CSS:

`html { scroll-behavior: smooth }`

(Edit to add: Not saying React Router is something you should / shouldn’t use. Just wanted to share that code in case it helps unblock anyone.)

This is a nice and simple solution! Just keep in mind that it'll interact badly with the browser's scroll-to-hash functionality.

A full solution is a little more tricky, and probably involves code on multiple components:

- You'll need something to scroll to the top or the current hash on load - You'll also need code on <Link> components to check if they link to the current URL/hash, and if they're pointing to the current hash, you'll need to scroll to it on click.

Here's some relevant scroll management code from Navi's components:

- https://github.com/frontarm/navi/blob/master/packages/react-...

- https://github.com/frontarm/navi/blob/master/packages/react-...

Accidentally downvoted on mobile (and upvoted two others). Thanks for this.

"Scroll Restoration" https://reacttraining.com/react-router/web/guides/scroll-res...

> It is most definitely not a one-liner.

  const LinkWrapper = ({to, ...rest}) =>
    to.startsWith(window.location.origin) ? <Link to={to} {...rest} /> : <a href={to} />;
> So do it for me. There’s no reason a routing library should break default behavior.

It's not default behavior for a client side router. Making it automatically scroll to the top would save you a few lines of code while creating a monumental headache for those who need it to retain the scroll position.

This has a number of bugs. Links don’t have to start with window.location.origin, they can just start with /. You didn’t pass in ...rest into the a tag, but oh did you know that the props of a are not the same type as the props to Link? They’re not - so even if you did that it wouldn’t have worked. Oh, and don’t forget to handle “javascript:” prefixed links - those are valid too. Oh and good luck getting this to type check with Typescript. You might be able to pull off something fancy with subtraction types, but who knows if it’ll continue to work when react router upgrades next year.

You might think I’m being overly pedantic. Perhaps I am - however in the course of my site I ran into each and every one of these issues after starting out with a clever one liner much like the one you provided. And every time I fixed one I wondered why react router couldn’t handle this for me.

> Making it automatically scroll to the top would save you a few lines of code while creating a monumental headache for those who need it to retain the scroll position.

Maybe I’m just not being charitable enough, but it beats me how a boolean flag is a “monumental headache” but all the above work with absolute links is an obvious thing anyone can do.

I understand the frustration.. that said, if you use a `javascript:` link in a modern web application, you deserve what you get (garbage).

I guess you might need a polyfill for window.location.origin depending on target browsers, and since they discussed relative urls earlier I guess they want to handle relative urls as well and probably want to also handle starts with //.

I think generally what happens, based on what I often end up doing and what I've seen in bigger projects that depend on React Router, is you make a linker component to handle issues, and in fairness you end up not having a 1 liner then.

I felt the same regret after each major upgrade where it seemed as breaking versions were due to unnecessary superficial API changes (which also left behind a trail of outdated docs/examples).

Ended up sticking through it as it appeared to be the default Router for React. It looks like there's some options now, so I'll consider moving if I have to suffer through another major breaking change.

> there's no relative routes https://github.com/ReactTraining/react-router/issues/2172

I've been managing "relative" routes by just using `match.path` and `match.url` from the previous route. Works fine.

    function Parent(props) {
      return (
          <Link to='/child'>Child</Link>
          <Route path='/child' component={Child}/>

    function Child({match}) {
      return (
          <Link to={`${match.url}/grandchild`}>Grandchild</Link>
          <Route path={`${match.path}/grandchild`} component={Grandchild} />

I also do this - 4 layers deep at times - and it works great. IMHO it's one of the more clever bits of react-router.

Yup. My "top level routes" only have 3 paths, and everything goes from there. Thinking about routes just like components is really fantastic for react, and I don't miss having a giant central router even a little bit.

I recently wrote a custom hook* which is serving me well so far.


I combine this with a simple switch statement in my main component and it does the job.

  const [page, setPage] = useRouter('/')

Yup. I only use react-router when I have to (work). My other projects I always roll my own. A typical html5 push router can be really small. I don’t reinvent the wheel normally but routing is hugely overcomplicated in the react ecosystem.

I feel like part of the problem with react libraries is that they implement their code "the react way" which basically means forcing it into your tree hierarchy. Dealing with dozens of higher order components gets really annoying after a while.

Doesn't much of this go away with hooks?

Before we had one hierarchy where everything was a component, logic and visuals.

Now we have two hierarchies where visuals are components and logic are hooks.

Yeah, this is definitely a thing. I'm a bit conflicted over it.

One one hand, staying within the world of React components gives you a lot of functionality for free. And with Suspense and Hooks, the amount you can do within a component has become even greater.

On the other hand, there are still things that you can't do within a component. For example, you can't easily run an async function once when the component mounts - which in my opinion, would be the perfect way to fetch data.

I've been building Navi to try and create a more natural way to map routes to views and a stream of data. The thing is, the more edge cases you cover, the closer it becomes to just building React from scratch.

At this point I still think that routing and data fetching is better handled externally from React, but it's less clear cut than it used to be.

I use preact in some of my projects and its router ("preact-router") actually does what it should:

- has an easy <Router onChange={...}> callback

- instead of <Link to> you can just use <a href> and it works for internal and external links

- routes are per default "exact", so "/profile" doesn't match "/profile/settings"

So much better, highly recommended. You probably need to alias "preact" to "react" in your webpack settings though.

Though not in a React project, I used router5 which also offers a react component. You might want to check that out as router5 has the features you mention.

I've been using the `connected-react-router` wrapper for redux with react-router, and it's been very nice to use... though my router component is getting really big at this point... but still understandable.

That would never be a mess like the router in Ionic 3 (they abandoned it in Ionic 4 a the bugs will never be fixed, it's a nightmare on a daily basis, and migrating from 3 to 4 is a nightmare also)

I usually shudder when I see React Router major versions which usually result in hard breaks that takes hours to migrate existing code bases to, so I'm pleasantly surprised to see:

> v5 is fully backwards compatible with 4.x

It appears the major version bump was due to package configuration changes instead of major API code changes.

Yeah that was my first reaction when I saw the title, "How hard is it going to be this time?" since it took me 2 days to migrate to the v4 last time.

Why upgrade?

Because if you don't upgrade, you are stuck with whatever version of React created at the time. You don't get stability & performance improvements, bug fixes, you can't potentially install some newer libraries requiring the new version. Not keeping up with upgrades is making the code rot slowly.

Because sooner of later you'll have to. Upgrading frequently minimizes the delta.

Same reaction here! Jumping from 2 -> 4 was a pretty big change to our codebase. Glad for this.

Apparently it was never meant to be a major bump, but they were forced to do it because of a semver screwup between their own packages.

All the changes were already in 4.4.0 published one day earlier, which they've now unpublished it seems.

We migrated from React Router to Reach Router (https://reach.tech/router) and found it to be better in a few ways:

* Built-in focus management.

* Smaller library size.

* Implicit route matching that Just Works.

* More comprehensive docs with live examples.

Migration was easy. Highly recommended.

So both of these routers are written by Ryan Florence? Interesting, what was the reason behind this? Freedom to experiment in one repo while keeping the other stable?

Ryan originally wrote react-router but then it became part of the ReactTraining org. Ryan later left ReactTraining to do his own thing, and started reach.tech, and Reach Router was born. Now Ryan is back at ReactTraining, and we’ll see what that means for the routers.

FWIW, I use Reach Router for everything now and greatly prefer it.

We did the same thing, then migrated back to React-Router. We had issues getting relative urls and redirects to work, examples were hard to come by, and generally the experience was just poorer than React-Router.

I did the same as you, initially reach router seemed like a great alternative, but it really doesn't feel finished, updates are few and far inbetween, and there is a large backlog of open issues / PRs.

I get the sense that Reach Router is no longer maintained despite what the project owner says, plus there's the fact that he's been rehired into the React Router team.

Well, at least one major project (Gatsby) uses it, so I bet it will stick around for a while yet. I don't think Gatsby could switch back to React-Router without introducing breaking changes.

nice to see they added a way to disable auto focus on route change, something that prevented me from using previously

This looks very similar to preact router.

This looks great! Thank you

react-router depends on the ReactTraining/history package for browser navigation support.

On 10 June 2017, an mind-bogglingly ill-conceived pull request[1] was merged, which causes invalid URLs to be pushed to the browser history, breaking the back button and page refresh in many scenarios.

Ever since, users have practically been begging the maintainer to fix the bug[2] and implementing hacky workarounds[3].

In a twist of irony, the contributor who originally introduced the bug has:

a) switched their own competing router package to a different history implementation[4]

b) argued that fixing the bug (which was introduced in a patch release) would require a major version bump, implying it would be better to let it remain broken[5].

I concede that the software is open source, and nobody is entitled to make any demands of the maintainers. Nevertheless, I pity the countless end-users who are left wondering why the most basic functionality of their web browsers is broken.

[1]: https://github.com/ReactTraining/history/pull/465

[2]: https://github.com/ReactTraining/history/issues/505

[3]: https://github.com/elastic/kibana/pull/32365

[4]: https://github.com/pshrmn/curi/commit/e850bcd9297398653a79b9...

[5]: https://github.com/ReactTraining/history/pull/656

This [0] indeed seems like such a rookie mistake, with all kind of edge cases ready to mess up the path encoding. The fact that they left such a broken behaviour in the lib for two years doesn't inspire much confidence.

0: https://github.com/ReactTraining/history/pull/465/files#diff...

I migrated away from react-router because from v3 to v4 broke everything that made sense. Things became wildly more complicated.

I'm now extremely happy with my new router of choice router5 (not to be confused with React Router 5) https://router5.js.org.

I did the same thing, although I might have migrated before v3.

I wrote 2 articles about it in case it can help someone:

- https://www.vincentprouillet.com/blog/testing-a-different-sp...

- https://www.vincentprouillet.com/blog/testing-a-different-sp...

It probably changed a bit since those articles but I was happy with it. A very simple codebase using that can be seen on https://github.com/Keats/hn-react

Thank you, this looks excellent! Finally a router that doesn't force you to define your UI by splitting everything up into their top-level view components. I want the router to give me the state, and then get the hell out of the way so I can fully manage the UI myself.

I've had a look at react-router multiple times over the years, and the best thing going for them seems to have been that they managed to squat that generic name early on. From the docs and GitHub, it's always been abundantly clear that the "current version sucks, mistakes were made, next version will totally fix everything" again and again.

I tried router5 in our team and found that while it is more powerful,

1. Having to write middleware for many things is quite hard for junior devs, as it requires deep understanding of how router works. (We have middleware for dependency injection, MobX per-page store, data fetching, auth check)

2. Due to the tree-based router design, some routes are impossible. See https://github.com/troch/route-node/issues/17 https://github.com/troch/route-node/issues/18

I still haven't found router of my choice yet. Maybe router5 without route-note could work for me, but I still wouldn't recommend it to any team.

Used v3 a few years ago at Linode and it was ok, if overly complex. The API changes resulting in v4 were so great that it basically became an entirely new project -- and the v4 docs are _still_ incomplete/not very helpful. Faced with learning a new project anyway I went with router5 at Capsule8.

Not only is router5's API simple, well-documented and easily programmable, the router5 folks have been receptive to feedback and pull requests. They also keep fairly well up-to-date compared to react-router (router5 supports React hooks, react-router doesn't yet).

I'm content with the direction and design of router5 and encourage others to check it out.

Router5 is awesome (but needs some getting used to). I remember being so fed up with React Router that I spent sometime implementing some functionality that existed in React Router before 1.x, and that still is missing (like route names).

And used router5 for that.

Didn't even need that many hacks: https://gist.github.com/dmitriid/675ceff4bd07ec6cdf06a560d72...

I’m still on v3 for that reason. On the surface, the immediate value proposition of v4 makes sense: use actual React Components instead of things that look like components but are not. In practice, it introduced so many breaking changes that, to me, it’s always felt like a solution to a problem that didn’t really exist.

Oh, this looks promising. I'm pretty tired of react-router, and I like to keep my navigation in my redux state, as it's a big part of any application state in my mind.

How well does router5 work with redux? I see they have an official package for it, so it's looking good, but maybe you have first hand experience.

Very well. We've been using it for 1.5yr at work, keeping up-to-date with latest React and Redux.

What I dislike most about React Router is how it pollutes rendering logic. They say it makes routing declarative. A switch statement is not declarative.

Hi reader unfamiliar with React Router! Just a heads up - these comments are not representative of typical React Router users. I work with React Router every day, and my co-workers and I love it.

Something about HN gives people license to air all of their nitpick grievances, and make it seem like SUcH A bIg DEaL OmG WorST LiBRarY EVArR!11

To the authors, maintainers, and contributors: Thank you for making the ecosystem better. It makes our work easier.

> Hey reader - stop reading these comments! They provide a cynical, unrepresentative viewpoint.

You could prefix this to every HN thread in existence.

React-Router is reasonably good, and has some issues - like most open source libraries.

Interesting to see the reason they had to do a major version bump, because of the usage of ^ in their internal dependencies meaning that you could get a mismatch between versions of the two internal sub packages react-router and react-router-dom.

The solution? Pinning the version to an exact number.

I always do this now as a policy, for everything, even when I'm using lockfiles though it shouldn't strictly be necessary, because I've been burned by ^ too many times. There's this ideal world expectation of ^ that it will magically give you upgrades for free without changes in behaviour, but breaking changes in behaviour occur all the time in minor and patch version updates in real world npm.

I think this is an intractable problem - mistakes will always be made, even when people try really hard, and over 100s of dependencies in a project you're therefore quite likely to see these mistakes fairly regularly.

I've never been a fan of React Router (mostly I don't get why defining routes with components is a good thing) and have been pleasantly surprised with how well Curi (https://curi.js.org/) works. It's nice to see it's not completely tied to a JS framework.

> I don't get why defining routes with components is a good thing

It makes the routes reactive. This is not necessary for smaller projects, but where it is needed this can be really helpful. The responsive route example shows this off well https://reacttraining.com/react-router/core/guides/philosoph...

I find the opposite true. Components based routing is fine for small applications, but in a sizable application you will inevitable end up with several things that doesn't fit nicely into the simplistic router component model, and now you're shit out of luck and have to try to jimmy-rig in some escape hatch.

It's a much more flexible model to have the router update your state in Redux or whatever, and then you take care of all the rendering and component hierarchies yourself.

Views should react to things like screen size, routes should route. There's really no relation between them and mixing these 2 functionalities has been a recipe for disaster in every app where I've seen it used because finding route definitions becomes a major chore. It also results in having duplicate route paths all over the place and renaming a path is painful.

We still use react-router but wrap it with code that generates all the routes off of our own object structure which describes the pages. Instead of using a string path to create links, we use page definition objects like this: `<PageLink to={Pages.user.account} />` and the custom PageLink component basically gets everything it needs to render the normal `<Link>` from the `account` object including the default display text of the link, authorization required to visit the page, etc.

Another thing we've had to do in every single project with React Router was to stop using the `history` prop and start creating our own `history` object that we can then import anywhere, not just in view code... We basically have a whole kit that wraps React Router at this point and it's much, much more predictable and maintainable than defining things in JSX.

That link suggests routing a user to a different page when they rotate their phone and not routing them back when they rotate it back. Rerouting a user just because they rotate their phone is a terrible idea.

That looks very, very similar to router5. Anything about it that stands out as better in Curi?

I recently implemented home grown routing in one of my apps - and was surpised to see that it was about 30 lines of simple code. You don't really need to tie yourself to a third party lib and risk future upgrades. Wrote about it here: https://medium.com/p/9e2c7b036b0

But if you don't want to risk future upgrades, you could also just not upgrade?

I think react-router in particular have been pretty good about sending this message with past major version changes.

I had nothing but trouble whenever I used React Router. For example, sometimes paths would be appended to the URL instead of replacing it, making it grow indefinitely, and it wasn't obvious why.

There were two API rewrites in a short space of time and it was extremely frustrating, especially since it seemed almost compulsory to be experienced with React Router (along with Redux) to be taken seriously by recruiters.

For my own projects, I started using page.js years ago and haven't looked back.


>sometimes paths would be appended to the URL instead of replacing it, making it grow indefinitely

Do you have a publicPath in your webpack.conf?

`publicPath: "/"`

Also make sure you a linking to="/path", not to="path"

Same here. I've had trouble with every version of React Router that I've used, and page.js seemed to always do pretty much exactly what I wanted.

I'm assuming that perhaps React Router is preferable beyond a certain amount of complexity? Can anyone chime in where page.js falls short?

Author here.

One more time for those in the back:

v0.x > v1 Breaking changes

v1 > v2 > v3 a couple obscure breaking changes, but practically none

18 months pass

v3 > v4 New, composable design, basically a new project

v3 still maintained, 3.5 years no changes. v4 2 years, no changes.

v4 -> v5 was supposed to be 4.4, but an internal dependency '^' got us, so best choice was version bump to prevent problems.

I think it was the fact that v3 to 4 was "basically a new project" with a new paradigm that earnt the reputation for breaking changes but then React Router is such a marketable name I can see why you'd be reluctant to spin off a new project.

I advise everyone to move away from React Router.

Maintainers who have such blatant disregard for users should not be rewarded or celebrated.

That's weird, I've had nothing but a great experience with the React Router team. Can you point to an example?

How is you using someone else's open source package "rewarding" them?

I think that when someone provides an open source project they are doing everyone a favour. We then have a choice not to use it. But using their free thing is certainly not doing them a favour unless we are somehow contributing back.

May I ask what are you making reference to? And do you happen to know a better library?

I'm surprised to see so many negative comments about react-router here. I've used react-router v4 in a half dozen projects without significant issues, and I think the API is pretty flexible and easy to work with. I'm glad to see the React Training folks continue working on it.

This is going to be awesome for React's apparent main industry: Training. A new set of videos and code camps...

Lol now that you mention this, it makes the React community make way more sense to me.

As someone who works with both React and VueJS on a daily basis, I find Vue Router to be much MUCH easier to work with, well documented and is really straightforward. Most importantly - It's an official package from VueJS core team and not a third party, so it's pretty much worry-free.

I totally agree. Vue Router always made more sense to me.

I wrote my own router for Inferno/React and Mobx inspired by Vue Router. It never made much sense to me to use components to define routes, or needing to use a HoC to be able to access the router from a component.

Probably the least stressful major version bump I've seen. Props to the team for solving the 4.4.x problem and pushing this out. Took a few minutes to update and now everything is great!

May I take this opportunity to recommend found? It’s great: https://github.com/4Catalyzer/found.

Here's an alternative which is being actively maintained: https://github.com/frontarm/navi

My heart warmed when they said they were maintaining backwards compatibility.

So many negative comments in here. Just wanted to let the maintainers know we use the library in multiple projects without issue and that we appreciate deeply the work that goes into it!

Anybody using ui-router? I used to use that one in angular and saw they have a react version now. But I'm not yet familiar enough with react to judge it.

Using React Component for routing has a benefit: Universal Routing, with all benefits of React inside the route itself (lifecycle, state, data fetching,...).

> There are no breaking changes in this release.

Nice, glad to see the project becoming more stable. Upgrading from v3 to v4 made us a lot problems...

Exemplary release notes.

Guidelines | FAQ | Support | API | Security | Lists | Bookmarklet | Legal | Apply to YC | Contact