{"id":29463614,"url":"https://github.com/fiddlerwoaroof/routedux","last_synced_at":"2025-07-14T05:38:08.877Z","repository":{"id":21938980,"uuid":"94355683","full_name":"fiddlerwoaroof/routedux","owner":"fiddlerwoaroof","description":"A router that maps urls to redux actions and vice-versa","archived":false,"fork":false,"pushed_at":"2025-07-09T17:48:53.000Z","size":5453,"stargazers_count":0,"open_issues_count":5,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-07-14T05:17:03.150Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"JavaScript","has_issues":false,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":"cjdev/routedux","license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/fiddlerwoaroof.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,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2017-06-14T17:20:44.000Z","updated_at":"2025-07-08T03:35:04.000Z","dependencies_parsed_at":"2023-11-08T10:38:59.521Z","dependency_job_id":"f986efaa-37c3-4639-9f21-451547cd78ec","html_url":"https://github.com/fiddlerwoaroof/routedux","commit_stats":null,"previous_names":[],"tags_count":32,"template":false,"template_full_name":null,"purl":"pkg:github/fiddlerwoaroof/routedux","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fiddlerwoaroof%2Froutedux","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fiddlerwoaroof%2Froutedux/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fiddlerwoaroof%2Froutedux/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fiddlerwoaroof%2Froutedux/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/fiddlerwoaroof","download_url":"https://codeload.github.com/fiddlerwoaroof/routedux/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fiddlerwoaroof%2Froutedux/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":265246229,"owners_count":23734111,"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":"2025-07-14T05:38:08.347Z","updated_at":"2025-07-14T05:38:08.860Z","avatar_url":"https://github.com/fiddlerwoaroof.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Routedux — Routes the Redux Way [![npm version](https://badge.fury.io/js/routedux.svg)](https://badge.fury.io/js/routedux) ![Node.js CI](https://github.com/cjdev/routedux/workflows/Node.js%20CI/badge.svg)\n\n\u003cimg alt=\"Route Dux\" src=\"https://upload.wikimedia.org/wikipedia/commons/thumb/d/da/Ducks_crossing_the_road_sign.png/92px-Ducks_crossing_the_road_sign.png\" align=\"right\" /\u003e\n\nRoutedux (:duck: :duck: :duck:) routes URLs to Redux actions and vice versa.\n\n(v1.x only works with React \u003e=16)\n\nYour application doesn't need to know it lives in a browser, but your\nusers want pretty urls and deep links.\n\n## Wait, my application doesn't need to know it lives in a browser?\n\nURLs are great for finding things on the internet. But a single page\napplication is not the same as a collection of resources that lives on\na remote server.\n\nA single page application is a web application only in the sense that\nit lives on the web. URLs are are not essential\nto it working well.\n\nURLs give users accessing your application in a browser the ability to\nbookmark a particular view in your application so that their\nexpectation of browser-based applications will continue to work.\n\nWe think that's a good thing, but we also don't think the idea of url\npaths should be littered through your application.\n\nWhen you are developing a redux application, you want your UI to be a\npure function of the current state tree.\n\nBy adding routes to that, it makes it harder to test. And this\ndifficulty can be compounded by other decisions about how to add\nroutes to your application.\n\n## An alternative approach\n\nReact Router is the currently-accepted way to do URL routing in React\napplications. For a standard React application without Redux, this\nsolution isn't too bad. But once you add Redux, things get difficult.\n\nWe basically discovered the same lessons as Formidable Labs:\n[React Router is the wrong way to route in Redux apps.](http://formidable.com/blog/2016/07/11/let-the-url-do-the-talking-part-1-the-pain-of-react-router-in-redux/)\n\nHowever, we don't think their solution\n([redux-little-router](https://github.com/FormidableLabs/redux-little-router))\ngoes far enough, as it still embeds the idea of routes throughout your\nuser interface.\n\nOnce you separate URLs from your application state, you can easily\nport it to other environments that don't know what URLs are, and by\nsimply removing the routing declaration, things will work as before.\n\nAs an added (and we think absolutely essential) benefit, your entire\napplication becomes easier to test, as rendering is a pure function of\nRedux state, and model logic and route actions are entirely\nencapsulated in Redux outside of the app.\n\n## Demo Site\n\nSee a simple [demo documentation site.](https://github.com/cjdev/routedux-docs-demo)\n\n## Simple Routing in 25 lines\n\n```javascript\nimport installBrowserRouter from 'routedux';\nimport {createStore, compose} from 'redux';\n\nconst LOAD_USER = 'LOAD_USER';\n\nfunction currentUserId() {\n  return 42;\n};\n\nfunction reduce(state = initialState(), action) {\n  ...\n}\n\nconst routesConfig = [\n  ['/user/:id', LOAD_USER, {}],\n  ['/user/me', LOAD_USER, {id: currentUserId()}],\n  ['/article/:slug', 'LOAD_ARTICLE', {}],\n  ['/', 'LOAD_ARTICLE', {slug: \"home-content\"}]\n];\n\nconst {enhancer, init} = installBrowserRouter(routesConfig);\n\nconst store = createStore(reduce, compose(\n  enhancer\n));\n\n//when you are ready to handle the initial page load (redux-saga and similar libraries necessitate this being separte)\ninit();\n\n```\n\nAny time a handled action fires the url in the address bar will\nchange, and if the url in the address bar changes the corresponding\naction will fire (unless the action was initiated by a url change).\n\n## Route matching precedence - which route matches best?\n\nRoute precedence is a function of the type of matching done in each\nsegment and the order in which the wildcard segments match. Exact\nmatches are always preferred to wildcards moving from left to right.\n\n```javascript\nconst routesInOrderOfPrecedence = [\n  [\"/user/me/update\", \"/user/me\"], // both perfectly specific - will match above any wildcard route\n  \"/user/me/:view\",\n  \"/user/:id/update\", // less specific because 'me' is exact match, while :id is a wildcard\n  \"/user/:id/:view\",\n];\n```\n\n## Fragment component\n\nGiven that every UI state will be in your state tree as a function of\nyour reducer logic, you can express any restriction on which parts of\nthe UI display, even those that have nothing to do with the specific\ntransformations caused by your URL actions.\n\n```javascript\n\nconst state = {\n  menu: ...\n}\n\nconst view = (\n  \u003cPageFrame\u003e\n      \u003cFragment state={state} filterOn=\"menu\"\u003e\n        \u003cMenu /\u003e\n      \u003c/Fragment\u003e\n  \u003c/PageFrame\u003e\n)\n\n// If menu is truthy, this renders as:\n\n(\n  \u003cPageFrame\u003e\n    \u003cMenu /\u003e\n  \u003c/PageFrame\u003e\n)\n\n// If menu is falsy, this renders as:\n(\n  \u003cPageFrame\u003e\n  \u003c/PageFrame\u003e\n)\n\n// If property is missing in path, it's falsy.\n\nconst view = (\n  \u003cPageFrame\u003e\n      \u003cFragment state={state} filterOn=\"menu.missingProp.something\"\u003e\n        \u003cMenu /\u003e\n      \u003c/Fragment\u003e\n  \u003c/PageFrame\u003e\n)\n\n// Renders as:\n(\n  \u003cPageFrame\u003e\n  \u003c/PageFrame\u003e\n)\n\n```\n\n## ActionLink and pathForAction(action)\n\nOccasionally it is nice to render URLs inside of your application.\n\nAs a convenience, we have attached `pathForAction` to the `store`\nobject, which uses the same matcher that the action matcher uses.\nThis allows you to create links in your application by using the\nactions.\n\n```javascript\nconst routesConfig = [\n  ['/user/:id', LOAD_USER, {}],\n  ['/user/me', LOAD_USER, {id: currentUserId()}]\n];\n/* ... do store initialization */\n\nstore.pathForAction({type:LOAD_USER, id: currentUserId()});\n/* returns /user/me */\n\n/* ActionLink */\n\nimport { ActionLink as _ActionLink } from \"routedux\";\n\nconst store = createStore(...);\n\nclass ActionLink extends _ActionLink {\n    constructor(props) {\n        super({ ...props });\n        this.store = store;\n    }\n}\n\nconst action = {\n  type: LOAD_USER,\n  id: 123\n};\n\nreturn (\n  \u003cActionLink action={action}\u003eLink Text\u003c/ActionLink\u003e\n);\n\n/* renders as a link to \u003ca href=\"/usr/123\"\u003eLink Text\u003c/a\u003e with the text */\n```\n\nNow you have links, but your links always stay up to date with your\nrouting configuration.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffiddlerwoaroof%2Froutedux","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffiddlerwoaroof%2Froutedux","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffiddlerwoaroof%2Froutedux/lists"}