{"id":17772630,"url":"https://github.com/jamesplease/router-comparison","last_synced_at":"2026-03-15T09:34:18.382Z","repository":{"id":66133911,"uuid":"40362138","full_name":"jamesplease/router-comparison","owner":"jamesplease","description":"WIP: A comparison of client side routers.","archived":false,"fork":false,"pushed_at":"2015-08-16T16:29:33.000Z","size":392,"stargazers_count":38,"open_issues_count":6,"forks_count":0,"subscribers_count":5,"default_branch":"master","last_synced_at":"2025-02-07T09:41:58.667Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":null,"has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/jamesplease.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2015-08-07T13:37:48.000Z","updated_at":"2022-07-06T18:54:29.000Z","dependencies_parsed_at":"2023-02-19T23:15:34.030Z","dependency_job_id":null,"html_url":"https://github.com/jamesplease/router-comparison","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jamesplease%2Frouter-comparison","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jamesplease%2Frouter-comparison/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jamesplease%2Frouter-comparison/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jamesplease%2Frouter-comparison/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/jamesplease","download_url":"https://codeload.github.com/jamesplease/router-comparison/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":246660081,"owners_count":20813338,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-10-26T21:40:09.930Z","updated_at":"2026-03-15T09:34:13.343Z","avatar_url":"https://github.com/jamesplease.png","language":null,"funding_links":[],"categories":[],"sub_categories":[],"readme":"# router-comparison\n\nWtf even are routers? This repository is an attempt to address that question by comparing the\nfeatures of the most popular client side routers.\n\n## Table of Contents\n\n- [About](#about)\n- [Why](#why)\n- [Routers Compared](#routers-compared)\n  - [Inspirations](#inspirations)\n  - [Omissions](#omissions)\n- [Features](#features)\n  - [DSL](#dsl)\n  - [Regex for `:dynamic`](#regex-for-dynamic)\n  - [Splats](#splats)\n  - [Optional segments](#optional-segments)\n  - [Unnamed segments](#unnamed-segments)\n  - [Regex instead of DSL](#regex-instead-of-dsl)\n  - [Asynchronous](#asynchronous)\n  - [Sort: specificity](#sort-specificity)\n  - [Sort: deepest](#sort-deepest)\n  - [Sort: order added](#sort-order-added)\n  - [Nested routes](#nested-routes)\n  - [Reset namespace](#reset-namespace)\n  - [Absolute URLs](#absolute-urls)\n  - [Optional history](#optional-history)\n  - [Includes history](#includes-history)\n  - [Cancel navigation](#cancel-navigation)\n  - [Redirect navigation](#redirect-navigation)\n  - [Template links](#template-links)\n  - [`.active` links](#active-links)\n  - [Query params](#query-params)\n  - ['Index' states](#index-states)\n  - ['Abstract' states](#abstract-states)\n  - ['Loading' states](#loading-states)\n  - ['Error' states](#error-states)\n  - ['Not Found' states](#not-found-states)\n  - [Pubsub](#pubsub)\n  - [Not found event](#not-found-event)\n  - [Scrolling](#scrolling)\n  - [Group data fetching](#group-data-fetching)\n  - [Enter hook](#enter-hook)\n  - [Exit hook](#exit-hook)\n  - [Update hook](#update-hook)\n\n## About\n\nRouters are playing an increasingly important role in client side apps. Many developers use the router\nto dictate what happens as the user navigates through the application – a substantial responsibility!\n\nPerhaps unsurprisingly, no two routers are the same. I created this chart to compare some of the features\nof the most popular, or otherwise noteworthy, routers.\n\n## Why\n\nNested routers are used in virtually every client side application these days. However, my\npreferred library, Backbone, does not have a nested router. In fact, you can't even build one from\nBackbone's router because of the way the code is structured ([I'm working to fix this in Backbone\nv2.0.0](https://github.com/jashkenas/backbone/pull/3660)).\n\nI want to build a new router that can be used in Backbone apps, and I want it to be the best it can\nbe. By examining existing routers, I can pluck the features I find most useful, and discard the ones\nthat I find less so.\n\n## Routers Compared\n\nThe following routers have been considered:\n\n- [Backbone](http://backbonejs.org/#Router)\n- [Ember](http://guides.emberjs.com/v1.10.0/routing/)\n- [React](https://github.com/rackt/react-router)\n- [UI-Router](https://github.com/angular-ui/ui-router)\n- [Stateman](https://github.com/leeluolee/stateman)\n- StateRouter\\*\n\n\\*This is a new stand-alone router that I'm building.\n\n#### Inspirations\n\nThroughout this document, I'll often draw comparisons between routers. This is usually because one of them was\ninspired by, and modeled after, the other. For this reason, their differences are of particular interest to me.\n\nEmber's router is the first nested router that I know of. UI-Router came shortly after, but it was largely an\nindependent endeavor. Nevertheless, both share similarities with one another, which is to be expected\nconsidering their similar approach to routing.\n\nEach of these original two routers has since inspired another popular router. Ember inspired the React Router,\nand UI-Router inspired Stateman.\n\nStateRouter is the name of my work-in-progress router. It's the reason that this document exists.\n\nAs a chart, the flow of influence might look something like:\n\n```\n UI-Router     Ember\n    |            |\n Stateman      React    ---(all of them)---\u003e StateRouter\n```\n\n#### Omissions\n\nI omitted certain routers from the above chart.\n\n##### Ampersand.js\n\nToo similar to Backbone's.\n\n##### Marionette.js\n\nToo similar to Backbone's.\n\n##### Angular.js v1\n\nI don't think many people use this (UI Router is more popular), and it's in the process of being\ncompletely replaced in Angular v2.\n\n##### Angular.js v2\n\nThe API for this router is still in flux, and it's difficult to find documentation on how it is intended to work.\n\n## Features\n\n### DSL\n\nBackbone | Ember | React | UI-Router | Stateman | StateRouter\n-------- | ----- | ----- | --------- | -------- | -----------\n✔        | ✔     | ✔     | ✔         | ✔        | ✔\n\nDSL stands for \"domain specific language.\" If you haven't heard that term before, don't worry: it's\na concept you may already be familiar with, even if you don't know the name. Example DSLs are the\ntemplating languages we use to output HTML, like Handlebars, Underscore, or Angular templates.\n\nDSLs are like mini programming languages that are designed to do one task.\n\nIn Routers, the DSL is the syntax you use to specify what sorts of URLs each route matches. For\ninstance, `books/:bookId` is an example of a DSL, where the `:` designates the start of a dynamic\nURL segment.\n\nEvery router comes with a DSL, and they share a common set of features. However, there are\ndifferences. The next few features in this list cover those differences.\n\n##### Thoughts\n\nDSLs are a no-brainer! The alternative would be writing regular expressions for everything, which is\npretty gross, I think. Given that pretty much every router supports their own DSL, it seems like\neveryone is in agreement on this point.\n\n### Regex for `:dynamic`\n\nBackbone | Ember | React | UI-Router | Stateman | StateRouter\n-------- | ----- | ----- | --------- | -------- | -----------\n✘        | ✘     | ✘     | ✔         | ✔        | ✔\n\nDynamic parts capture a single URL segment within a path. A URL segment are each of the sections\nseparated by `/`. So, for instance, in the URL `\"/books/2\"`, there are two segments: `books` and\n`2`.\n\nGenerally, dynamic segments capture **anything** between the parenthesis. But some libraries allow\nyou to change what's matched by a dynamic segment with a regex and/or type annotation.\n\nUI-Router is the first library that I know of to do this. Because Stateman is based off of\nUI-Router, it, too, has a similar feature.\n\nAn example in UI-Router is:\n\n```js\n\"/user/{id:[0-9a-fA-F]{1,8}}\"\"\n```\n\nUI-Router has an extra feature not found in Stateman: it allows you to specify a type, too. These\nare like built-in regexes that you can reference. So, for instance, `'/calendar/{start:date}'` is a\nvalid route in UI-Router (but not Stateman).\n\nAlthough Backbone does not support this feature, it does support [using a regular expression for the\nwhole route URL instead of the DSL.](#regex-instead-of-dsl).\n\nEmber and React, on the other hand, don't offer anything like this. Shucks!\n\n##### Thoughts\n\nThis feature is super useful, I think. It allows the developer to move validation into the\nroute itself, maximizing the number of invalid requests that are automatically sent off to the 404\nroute.\n\nWhen routers don't support this, the developer is forced to write custom validation logic in each\nroute, which is more work for them.\n\n### Splats\n\nBackbone | Ember | React | UI-Router | Stateman | StateRouter\n-------- | ----- | ----- | --------- | -------- | -----------\n✔        | ✔     | ✔     | ✔         | ✘        | ✔\n\nSplats can be used to capture more than one segment of the URL, and are a denoted by `*`. An\nexample is:\n\n`\"/users/*splat\"`\n\nThis would match `/users/sandwich`, as well as `/users/james/picture`, and even\n`/users/james/picture/edit`.\n\nSplats are interesting because of the way different routers treat them. In Backbone and Ember,\nthey're of particular importance, because they're the way that you specify 404 routes.\n\n`\"*notFound\"`\n\nThis works really well for Ember, but not so well for Backbone. The difference is that Ember has a\nunique matching algorithm: less specific routes are matched last (see:\n[sort by specificity]((#sort-specificity))). So even if you specify the 404 route first, it won't be\nmatched unless nothing else does.\n\nBackbone isn't so smart. It matches on a first come, first serve basis, which requires you to place\nthe 404 route last. In practice, this can end up being a tedious requirement to fulfill.\n\nOther libraries use one of two other abstractions for 404s: either [a 404 state](#not-found-states),\nand/or [a pubsub event](#not-found-event) that is fired on the router itself.\n\n##### Thoughts\n\nIn the applications I have developed, splats have only been useful to match 404 routes. There might\nbe some good use cases I simply haven't encountered, though, so I intend to include them in \nStateRouter. Additionally, I intend for them to be the algorithm behind\n[404 states](#not-found-states) in StateRouter.\n\nIf you've used splats successfully in an app for a feature other than 404 routes, do let me know\nby raising an issue! I'm curious to hear the use case.\n\nAlthough 404 states can be implemented as thin abstraction layers over splats, I think there is\nvalue in that. An explicit 404 state is far more expressive than using splats directly to\nrepresent 404 states.\n\n### Optional segments\n\nBackbone | Ember | React | UI-Router | Stateman | StateRouter\n-------- | ----- | ----- | --------- | -------- | -----------\n✔        | ✘     | ✔     | ✔         | ✘        | ✔\n\nOptional segments are, well, exactly what you'd expect: segments that don't need to be there.\nThey're generally designated by a `?` or parentheses.\n\nUI-Router also supports optional segments, albeit in a different fashion. When a parameter\nis given a default value (which can be set when you configure your route), then it is made optional.\n\nAn example from Backbone, which uses parentheses:\n\n`docs/:section(/:subsection)`\n\n##### Thoughts\n\nThese haven't been particularly useful to me, but it seems like many other developers disagree. There's\n[an open issue](https://github.com/tildeio/route-recognizer/issues/60) to support these in Ember, for\ninstance. So, because I don't want StateRouter to be the only router on the street that doesn't support\nthese things, they'll def. be in there.\n\n### Unnamed segments\n\nBackbone | Ember | React | UI-Router | Stateman | StateRouter\n-------- | ----- | ----- | --------- | -------- | -----------\n✘        | ✘     | ✘     | ✘         | ✔        | ✘\n\nAn unnamed segment is a dynamic segment, or a splat, that doesn't require an identifying name. For\ninstance, in the route URL `\"books/:bookId\"`, `bookId` is the name of the dynamic segment.\n\nStateman added this feature, although it does not exist in the UI-Router.\n\n##### Thoughts\n\nI like being explicit, so I don't intend to add this feature to StateRouter.\n\n### Regex instead of DSL\n\nBackbone | Ember | React | UI-Router | Stateman | StateRouter\n-------- | ----- | ----- | --------- | -------- | -----------\n✔        | ✘     | ✘     | ✔         | ✘        | ✘\n\nBackbone and UI-Router allow you to write a regular expression instead of the DSL. As you would expect,\nthis gets ugly quickly. Because of the small amount of code necessary to implement the feature, it does\nfit in with Backbone's minimalist ideas. It's comparable, I think, to allowing\n[regex in the dynamic segments.]((#regex-for-dynamic))\n\n##### Thoughts\n\nThis is another one of those features that is difficult for me to think up of a good use case for. If\nthe DSL is powerful enough, it should not be necessary for a developer to use Regex directly.\n\nNevertheless, I would be interested in supporting this feature, but it seems to be incompatible with\n[sorting by specificity](#sort-specificity), which I find more valuable.\n\n### Asynchronous\n\nBackbone | Ember | React | UI-Router | Stateman | StateRouter\n-------- | ----- | ----- | --------- | -------- | -----------\n✔*       | ✔     | ✔     | ✔         | ✔        | ✔\n\nWhether or not the route handlers are asynchronous. This is important for routers that support\nnesting, because you'll often be fetching data in your handlers.\n\nBecause Backbone's handlers are independent, it simply doesn't matter if your callback is\nasynchronous or synchronous: nothing really *depends* on them.\n\n##### Thoughts\n\nConsidering that most routers are intended to be used to orchestrate data fetching, it only makes\nsense that they're built to support asynchronous handlers!\n\n### Sort: specificity\n\nBackbone | Ember | React | UI-Router | Stateman | StateRouter\n-------- | ----- | ----- | --------- | -------- | -----------\n✘        | ✔     | ✘*    | ✘         | ✘        | ✔\n\nSorting is the order in which the router attempts to find a route. If the user has two\nroutes corresponding to the following two URLs:\n\n`books/:bookId` and `books/author`\n\nWhich will match when the user navigates to `books/author`? Does it depend on the order\nthat they are added?\n\nFor most routers, the answer to the above question is **yes**. `books/author` will\nneed to be added to the router before `books/:bookId`, or else the dynamic segment\nwill always match the string.\n\nFor routers that support [regex for dynamic segments](#regex-for-dynamic), this matters less because\nthat feature provides a way to make your dynamic segments even more specific.\n\nEmber and my own router will implement this feature.\n\nReact Router uses an algorithm that indirectly leads to sorting by specificity. Their\nalgorithm attempts to match [the deepest route first.](#sort-deepest)\n\n##### Thoughts\n\n:+1:!\n\n\n### Sort: deepest\n\nBackbone | Ember | React | UI-Router | Stateman | StateRouter\n-------- | ----- | ----- | --------- | -------- | -----------\n✘        | ✘*    | ✔     | ✘         | ✘        | ✘*\n\nSorting by the deepest route is similar to sorting by specificity. It tries to\nmatch the most-nested routes first.\n\nBecause deeper routes tend to be more specific, Ember's algorithm approximates\nthis one.\n\n##### Thoughts\n\nSort by specificity is more intuitable, I think, so I intend to use that instead.\n\n### Sort: order added\n\nBackbone | Ember | React | UI-Router | Stateman | StateRouter\n-------- | ----- | ----- | --------- | -------- | -----------\n✔        | ✘     | ✘     | ✔         | ✔        | ✘ \n\nAll other routers sort by the order added. Therefore, you must specify your dynamic segments and\nsplats last, or use regexes to restrict what your dynamic segments match.\n\n##### Thoughts\n\nThis is easier to implement than sorting by specificity, but more difficult for developers to work\nwith. However, for Routers that allow you override the regex used by dynamic segments, this is less\nof an issue.\n\n### Nested routes\n\nBackbone | Ember | React | UI-Router | Stateman | StateRouter\n-------- | ----- | ----- | --------- | -------- | -----------\n✘        | ✔     | ✔     | ✔         | ✔        | ✔\n\nAre the routes related? Does entering a child route cause you to first enter the parent route? If\nyes, then it's a nested router.\n\nGiven that routes encode your app's *location* (which is often represented by a URL), it's no\nsurprise that most routers these days are nested.\n\nBackbone is the exception here.\n\n##### Thoughts\n\nAll contemporary routers support nesting because it is a super useful feature!\n\n### Reset Namespace\n\nBackbone | Ember | React | UI-Router | Stateman | StateRouter\n-------- | ----- | ----- | --------- | -------- | -----------\n✘        | ✔     | ✘     | ✘         | ✘        | ✘  \n\nMost routers require that children routes be built from their parent's namespace. For instance, if\nyou want to add a child route to `books.book`, it must necessarily begin with `books.book`. A route\ncalled `comments` could not, for instance, be a child of `books.book`.\n\n##### Thoughts\n\nI don't think that this is particularly useful. A friend of mine on the Ember Data team suggests\nthat nobody use this feature, which is pretty telling, I think.\n\n### Absolute URLs\n\nBackbone | Ember | React | UI-Router | Stateman | StateRouter\n-------- | ----- | ----- | --------- | -------- | -----------\n✔        | ✘     | ✔     | ✔         | ✔        | ✔  \n\nA child route's URL is often appended to the parent's route. Are you able to specify that the child's\nURL is an absolute URL?\n\nBackbone's URLs are independent, so, yes, they are all absolute.\n\nIn UI-Router and Stateman, you can use the `^` prefix to indicate that the URL is absolute.\n\nIn React Router, prefixing with `/` indicates an absolute URL, whereas no prefix is not an\nabsolute route.\n\n##### Thoughts\n\nI haven't worked on an app that would use this, but it is used by React and UI-Routers, so I'll\nlikely include it.\n\n### Optional history\n\nBackbone | Ember | React | UI-Router | Stateman | StateRouter\n-------- | ----- | ----- | --------- | -------- | -----------\n✘        | ✔     | ✘     | ✔         | ✘        | ✔\n\nBecause a nested router is just a state machine, it should not logically need to depend on URLs to\nfunction. This is useful in apps that don't encode the location of the user in the URL, like\nembedded apps.\n\nInterestingly, Ember and the UI-Router are the only two that support this feature. The two libraries\nthat were inspired by these libraries, React's Router and Stateman, seem to have foregone including\nit.\n\n##### Thoughts\n\nThis feature is a must! It's a shame that the React Router and Stateman didn't recognize the value.\n\n### Includes history\n\nBackbone | Ember | React | UI-Router | Stateman | StateRouter\n-------- | ----- | ----- | --------- | -------- | -----------\n✔        | ✔     | ✔     | ✔*        | ✔        | ✘\n\nWhether or not the library includes a History implementation (to support, say, hash changes in old\nbrowsers).\n\nGenerally, the routers that are bundled with a framework do include a history implementation.\n\nUI-Router uses Angular's `$location` server.\n\n##### Thoughts\n\nVery useful for routers that are part of a framework. Because StateRouter is just a router, it\ndoesn't make sense to bundle up a history implementation for it. A Backbone-specific version will\nbe subsequently released that ties in nicely with Backbone's history. Otherwise, you'll need\nto wire up your own history, which won't be too hard.\n\n### Cancel navigation\n\nBackbone | Ember | React | UI-Router | Stateman | StateRouter\n-------- | ----- | ----- | --------- | -------- | -----------\n✔*       | ✔     | ✔     | ✔         | ✔        | ✔\n\nWhether or not there's an abstraction for canceling navigation.\n\nIn Ember and React Router you get the opportunity for each route to specify whether the transition\nshould be cancelled or not.\n\nIn UI-Router and Stateman, an event is fired on the router itself that allows you to cancel the\ntransition, so it's more like a global setting. v1 of the UI-Router (unreleased as of 8/16) will\nallow cancellation at any level of granularity.\n\nBackbone provides a method that you can return false from to prevent transitioning.\n\n##### Thoughts\n\nPer-route cancellation is fantastic. Global cancellation...less so.\n\n### Redirect navigation\n\nBackbone | Ember | React | UI-Router | Stateman | StateRouter\n-------- | ----- | ----- | --------- | -------- | -----------\n✔        | ✔     | ✔     | ✔*        | ✔        | ✔\n\nVery similar to the above: can you stop the transition and instead redirect? The same hooks used for\ncanceling is used for redirection in all libraries.\n\nThe UI-Router had\n[a bad bug with redirecting](https://github.com/angular-ui/ui-router/issues/326#issuecomment-52731196)\nthe last time I used it, and I do not believe that it has been fixed.\n\n##### Thoughts\n\nSame as above.\n\n### Template links\n\nBackbone | Ember | React | UI-Router | Stateman | StateRouter\n-------- | ----- | ----- | --------- | -------- | -----------\n✘        | ✔     | ✔     | ✔         | ✘        | ✘\n\nSome libraries provide an abstraction to be used in your template to generate links that are\nautomagically hooked up to the router.\n\nThis feature is super rad – if you haven't used it before you should play with one of the routers\nthat support it.\n\n##### Thoughts\n\nSo, so good, but out of scope for a standalone router, I think. I plan to build a version of\nStateRouter exclusively for Backbone apps, and it will have templating features like this.\n\n### `.active` links\n\nBackbone | Ember | React | UI-Router | Stateman | StateRouter\n-------- | ----- | ----- | --------- | -------- | -----------\n✘        | ✔     | ✔     | ✔         | ✘        | ✘\n\nDoes the router automatically append an active class to the links in the template when that state is\nentered?\n\n##### Thoughts\n\n:heart_eyes: Although I love this feature, it won't be in the core StateRouter library, given just\nhow many templating languages there are out there.\n\n### Query params\n\nBackbone | Ember | React | UI-Router | Stateman | StateRouter\n-------- | ----- | ----- | --------- | -------- | -----------\n✘        | ✔     | ✔     | ✔         | ✔        | ✔\n\nComing soon...\n\n##### Thoughts\n\nComing soon...\n\n### 'Index' states\n\nBackbone | Ember | React | UI-Router | Stateman | StateRouter\n-------- | ----- | ----- | --------- | -------- | -----------\n✘        | ✔     | ✔     | ✘         | ✘        | ✔ \n\nTo understand index states, you must first know that nested routers involve states being active\nor inactive. Because they are nested, when a child is landed on, all of its parents are *also*\nactive.\n\nIndex states are special states that can be associated with each state. They're only activated\nwhen their associated state is landed on directly.\n\nI understand that this may be confusing, so let's look at an example.\n\nConsider three routes,\n\n`books`\n`books.index`\n`books.book`\n\nGiven these states, landing on \"books\" will activate both books and books.index.\n\nHowever, landing on \"books.book\" will activate both \"books\" and \"books.book\", but not \"books.index\"\n\n##### Thoughts\n\n:+1: This is a super useful feature to better control the fetching of data.\n\n### 'Abstract' states\n\nBackbone | Ember | React | UI-Router | Stateman | StateRouter\n-------- | ----- | ----- | --------- | -------- | -----------\n✘        | ✘     | ✘     | ✔         | ✘        | ✘\n\nAbstract states are maybe even more difficult to understand than index states. Abstract states are\nstates that cannot ever be landed on directly. Instead, they can only be activated when their child\nstates are.\n\nThis gives you a hook to, say, load a particular set of data for a group of sibling routes.\n\n##### Thoughts\n\nIt's my understanding that these provide the same value as index states, but in a way that I find\nto be more confusing.\n\n### 'Loading' states\n\nBackbone | Ember | React | UI-Router | Stateman | StateRouter\n-------- | ----- | ----- | --------- | -------- | -----------\n✘        | ✔     | ✘     | ✘         | ✘        | ✔\n\nWhile transitions are in progress some routers give you a `loading` state to display.\n\n##### Thoughts\n\n:+1:\n\n### 'Error' states\n\nBackbone | Ember | React | UI-Router | Stateman | StateRouter\n-------- | ----- | ----- | --------- | -------- | -----------\n✘        | ✔     | ✘     | ✘*        | ✘*       | ✔\n\nWhen an error occurs when doing a transition, some routers allow you to specify an `error` state to\nenter.\n\nUI-Router and Stateman provide events to manage this.\n\n##### Thoughts\n\n:+1:\n\n### 'Not Found' states\n\nBackbone | Ember | React | UI-Router | Stateman | StateRouter\n-------- | ----- | ----- | --------- | -------- | -----------\n✘        | ✘     | ✔     | ✘         | ✔*       | ✔\n\nNot found states let you specify unique not found states for each portion of your\napplication.\n\nStateman provides a not found state that only exists on the root level, so it's substantially\nless useful than what is provided by React.\n\n##### Thoughts\n\nVery useful when done on a per-route basis.\n\n### Pubsub\n\nBackbone | Ember | React | UI-Router | Stateman | StateRouter\n-------- | ----- | ----- | --------- | -------- | -----------\n✔        | ✘     | ✘     | ✔         | ✔        | ✘\n\nWhether or not you can do something like:\n\n`router.on('event', myCallback)`\n\nThese routers are generally the ones that give you global hooks to catch errors and 404 states, as\nthose hooks are events.\n\n##### Thoughts\n\nIf the router is for another library that has pubsub, then I think that supplying events for\nconsistency's sake makes sense. Otherwise, I think the majority of routing features don't benefit\nmuch from including pubsub.\n\nOne issue with pubsub is that everything is globally configured. Instead of associating logic\nwith a particular route, it requires that you use if-then statements in a global callback to\nspecify per-route behaviors.\n\n### Not found event\n\nBackbone | Ember | React | UI-Router | Stateman | StateRouter\n-------- | ----- | ----- | --------- | -------- | -----------\n✘        | ✘     | ✘     | ✔         | ✔        | ✘\n\nNot found events are triggered on the router directly, and give you a global hook to manage\n404s. This is less powerful than Not Found states, because it requires that you place all of your\n404 logic within a single callback.\n\n##### Thoughts\n\n:-1:\n\n### Scrolling\n\nBackbone | Ember | React | UI-Router | Stateman | StateRouter\n-------- | ----- | ----- | --------- | -------- | -----------\n✘        | ✘     | ✔     | ✔*        | ✘        | ?\n\nBrowsers remember your scroll position when you navigate an app with the Back and Forward buttons.\nThey also scroll you to the top of the page when a new link is clicked.\n\nThis feature is about whether or not the router supports this out of the box.\n\nThe UI-Router is markedly different: it allows you to scroll to a particular element when the route\nchanges.\n\n##### Thoughts\n\nDefinitely useful, but I'm torn on whether or not it's a good idea to bundle this into the router\ndirectly!\n\nUI-Router's scrolling feature might be useful in some situations, but I don't think that it's\nuseful as a default, or even as a feature to include in a core routing library.\n\n### Group data fetching\n\nBackbone | Ember | React | UI-Router | Stateman | StateRouter\n-------- | ----- | ----- | --------- | -------- | -----------\n✘        | ✔     | ✔*    | ✘*        | ✘        | ?\n\nIf 3 routes are activated, and each specify data to be fetched, does all of the fetching happen\nbefore views start rendering?\n\nEmber does this out of the box. React, being just a view layer, naturally doesn't have any APIs\ndirectly related to fetching data. But with some pretty messy boilerplate code you can pull it off\n(see the examples under [the Router.run method](http://rackt.github.io/react-router/#Router.run))\n\nUI-Router's unreleased v1 will allow you to specify 'policies' to manage this.\n\n##### Thoughts\n\nI'm on the fence about this one. I like the idea of fetching all of the data first to run the XHRs\nconcurrently, but I'm worried that it may make passing each route the data that it requested more\ndifficult.\n\nI'm leaning more toward grouping them, though.\n\n### Enter hook\n\nBackbone | Ember | React | UI-Router | Stateman | StateRouter\n-------- | ----- | ----- | --------- | -------- | -----------\n✔        | ✔     | ✔*    | ✔         | ✔        | ✔ \n\nDoes a callback fire when the route is entered?\n\nIn Backbone, this is all that you get.\n\nIn React, from what I understand it sort of just implicitly renders the component. I'm not sure if\nthere's much else that you'd want to do (or could do), but I'm only gathering this from their docs\nas I haven't used their router.\n\nIn Ember, this is a no-op that you can use to include additional logic.\n\n##### Thoughts\n\nA good idea.\n\n### Exit hook\n\nBackbone | Ember | React | UI-Router | Stateman | StateRouter\n-------- | ----- | ----- | --------- | -------- | -----------\n✘        | ✔     | ✘     | ✔         | ✔        | ✔ \n\nWhen a route is being transitioned out, do you get a hook to do things?\n\nOne thing to note is that this seems to be conflated with the ability to cancel/redirect in certain\nrouters. In the React Router, for instance, there's no distinct \"exit\" hook, but there is a hook\ncalled before you leave, which is intended for canceling. I don't know enough about React to know\nwhether this is a good or a bad thing!\n\nIn Backbone, there's no such thing as a route being 'active' or 'inactive.' Routes are just\ncallbacks out-of-the-box.\n\n##### Thoughts\n\nVery useful for teardown when you're working with a nested router.\n\n### Update hook\n\nBackbone | Ember | React | UI-Router | Stateman | StateRouter\n-------- | ----- | ----- | --------- | -------- | -----------\n✘        | ✔     | ✘     | ✘         | ✔        | ✔ \n\nAn update hook is distinct from an enter hook, in that it is generally related to whether the\n\"context\" of a route changes. Consider a route with a URL `:bookId`. When the user transitions to\nthis same route, but with a different ID, what happens?\n\nIn Ember, the update hook is the most important hook. It's where the view is rendered, for instance.\nThe update hook in Ember is called `setup`. And the order of callbacks goes\n`exit =\u003e enter =\u003e setup`\n\n`exit` starts at the deepest route, and works its way up. `enter` and `setup` work their way\ndown the chain. `setup` begins on the first route whose context is changed.\n\nStateman operates in a different way. The order goes `exit =\u003e update =\u003e enter`. And, unlike\nEmber, `update` is called on any route that is unchanged, even if the context hasn't changed.\nAlso, the `update` hook in Stateman works its way the tree, like the `exit` hook.\n\n##### Thoughts\n\n:+1: It is useful to differentiate entering and updating, I think. I tend to prefer Ember's\nhook structure.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjamesplease%2Frouter-comparison","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjamesplease%2Frouter-comparison","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjamesplease%2Frouter-comparison/lists"}