{"id":21862279,"url":"https://github.com/innfactory/react-typesafe-routes","last_synced_at":"2025-07-27T16:12:01.448Z","repository":{"id":41584773,"uuid":"305365293","full_name":"innFactory/react-typesafe-routes","owner":"innFactory","description":"The last routing library you will ever need in your React projects. (At least if you're using react-router–dom but also why wouldn't you?)","archived":false,"fork":false,"pushed_at":"2024-11-26T15:46:10.000Z","size":3360,"stargazers_count":13,"open_issues_count":6,"forks_count":2,"subscribers_count":4,"default_branch":"main","last_synced_at":"2025-04-12T04:29:22.044Z","etag":null,"topics":["react","react-router","react-router-dom","routes"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","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/innFactory.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":"2020-10-19T11:44:56.000Z","updated_at":"2022-05-25T13:07:50.000Z","dependencies_parsed_at":"2023-02-08T12:31:37.573Z","dependency_job_id":null,"html_url":"https://github.com/innFactory/react-typesafe-routes","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/innFactory%2Freact-typesafe-routes","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/innFactory%2Freact-typesafe-routes/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/innFactory%2Freact-typesafe-routes/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/innFactory%2Freact-typesafe-routes/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/innFactory","download_url":"https://codeload.github.com/innFactory/react-typesafe-routes/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248948444,"owners_count":21187873,"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":["react","react-router","react-router-dom","routes"],"created_at":"2024-11-28T03:14:33.179Z","updated_at":"2025-04-14T19:41:16.228Z","avatar_url":"https://github.com/innFactory.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# React (Awesome) Typesafe Routes\n\n![CI](https://github.com/innFactory/react-typesafe-routes/workflows/CI/badge.svg)\n[![Version](https://img.shields.io/npm/v/react-typesafe-routes.svg)](https://www.npmjs.com/package/react-typesafe-routes)\n[![Downloads](https://img.shields.io/npm/dt/react-typesafe-routes.svg)](https://www.npmjs.com/package/react-typesafe-routes)\n\nThe last routing library you will ever need in your React projects. (At least if you're using react-router–dom but also why wouldn't you?)\n\n[Typedoc documentation](https://innfactory.github.io/react-typesafe-routes)\n\n## Table of Contents\n\n- [React (Awesome) Typesafe Routes](#react-awesome-typesafe-routes)\n  - [Table of Contents](#table-of-contents)\n  - [Installing](#installing)\n  - [Example](#example)\n    - [Router definition](#router-definition)\n    - [Usage in App](#usage-in-app)\n    - [Route programmatically](#route-programmatically)\n  - [Types of Routers](#types-of-routers)\n    - [OptionsRouter](#optionsrouter)\n    - [Router](#router)\n  - [Routes](#routes)\n    - [Route Template and Parameters](#route-template-and-parameters)\n      - [Basic parameters](#basic-parameters)\n      - [Optional parameters](#optional-parameters)\n      - [Query parameters](#query-parameters)\n      - [Child Routes](#child-routes)\n  - [Parameter Parsers](#parameter-parsers)\n    - [Available Parsers](#available-parsers)\n    - [Your own parser](#your-own-parser)\n  - [Hooks](#hooks)\n    - [useRouteOptions](#userouteoptions)\n    - [useRouteParams](#userouteparams)\n    - [useRouteActive and useRoutesActive](#userouteactive-and-useroutesactive)\n  - [Components](#components)\n    - [RouterSwitch](#routerswitch)\n    - [Link](#link)\n    - [NavLink](#navlink)\n    - [Redirect](#redirect)\n    - [Route](#route)\n  - [Roadmap](#roadmap)\n  - [Contributing](#contributing)\n  - [License](#license)\n\n## Installing\n\nMake sure you are using at least `Typescript 4.1.2` in your project. To find out what version you are using use `npm ls typescript`. There is a known issue with `react-scripts 4.0.1` still requiring `Typescript 3` but you can circumvent that by adding a `--legacy-peer-deps` to your install command.\n\n```sh\nnpm install react-typesafe-routes\n\nor\n\nyarn add react-typesafe-routes\n```\n\n## Example\n\n### Router definition\n\n```tsx\nconst defaultOptions = {\n  appBar: true,\n};\n\nconst AuthMiddleware: RouteMiddleware = next =\u003e {\n  if (isAuthenticated) {\n    return next;\n  } else {\n    return () =\u003e \u003cRedirect to={router.login()} /\u003e;\n  }\n};\n\nexport const router = OptionsRouter(defaultOptions, route =\u003e ({\n  home: route('/', {\n    component: HomePage,\n  }),\n  login: route('/login', {\n    component: LoginPage,\n    options: { appBar: false },\n  }),\n  players: route(\n    '/players',\n    {\n      component: PlayersPage,\n      middleware: AuthMiddleware,\n    },\n    route =\u003e ({\n      info: route(\n        '/:name/:id',\n        {\n          component: PlayerInfoPage,\n          params: {\n            name: stringParser,\n            id: intParser,\n          },\n        },\n        route =\u003e ({\n          rating: route('/rating/:id', {\n            component: PlayerRatingPage,\n            params: { id: intParser },\n          }),\n          ban: route('/rating/:id', {\n            component: PlayerRatingPage,\n            params: { id: intParser },\n          }),\n        })\n      ),\n    })\n  ),\n}));\n```\n\n### Usage in App\n\nThe BrowserRouter comes from `react-router-dom` you can use any Router from that package that you like.\n\n```tsx\nconst AppBar = () =\u003e {\n  return (\n    \u003cdiv\u003e\n      \u003cul\u003e\n        \u003cli\u003e\n          \u003cLink to={router.home()}\u003eHome\u003c/Link\u003e\n        \u003c/li\u003e\n        \u003cli\u003e\n          \u003cLink to={router.player()}\u003ePlayers\u003c/Link\u003e\n        \u003c/li\u003e\n      \u003c/ul\u003e\n    \u003c/div\u003e\n  );\n};\n\nconst App = () =\u003e {\n  const { appBar } = useRouteOptions(router);\n\n  return (\n    \u003cBrowserRouter\u003e\n      \u003cdiv\u003e\n        {appBar \u0026\u0026 \u003cAppBar /\u003e}\n        \u003cRouterSwitch router={router} /\u003e\n      \u003c/div\u003e\n    \u003c/BrowserRouter\u003e\n  );\n};\n```\n\n### Route programmatically\n\nTo go to a route programmatically / without a `Link` Component:\n\n```ts\nconst navigate = useNavigate();\nnavigate(router.players().player({ id: 1, name: 'playerName' }).$);\n```\n\nThe function will require you to input required parameters and don't forget the dollar sign at the end.\n\n## Types of Routers\n\n### OptionsRouter\n\nThis is the router most people will probably use. It supports Global options that configurable on a per Route basis and they automatically apply for child routes.\n\nFor example the login route is supposed to be full screen and doesn't require the AppBar.\n\n```tsx\nconst defaultOptions = {\n  appBar: true,\n};\n\nconst router = OptionsRouter(defaultOptions, route =\u003e ({\n  home: route('/', {\n    component: HomePage,\n  }),\n  login: route('/login', {\n    component: LoginPage,\n    options: { appBar: false }\n  }),\n});\n\nconst App = () =\u003e {\n  const options = useRouteOptions(router);\n\n  return (\n    \u003cdiv\u003e\n      {options.appBar \u0026\u0026 \u003cAppBar\u003e}\n      \u003cRouterSwitch router={router} /\u003e\n    \u003c/div\u003e\n  );\n};\n```\n\n### Router\n\nThe Router is basically the same as the OptionsRouter but it doesn't have Options as the name already implied. No idea why you would need this but it's there just in case.\n\n```tsx\nconst router = Router(route =\u003e ({\n  home: route('/', {\n    component: HomePage,\n  }),\n  login: route('/login', {\n    component: LoginPage,\n  }),\n});\n\nconst App = () =\u003e {\n  return (\n    \u003cdiv\u003e\n      \u003cRouterSwitch router={router} /\u003e\n    \u003c/div\u003e\n  );\n};\n```\n\n## Routes\n\nRoutes can only be create inside an [OptionsRouter](#optionsrouter) or a [Router](#router).\n\n```tsx\nconst options = { appBar: true };\nconst router = OptionsRouter(options, route =\u003e ({\n  home: route(\n    /**\n     * The route template\n    */\n    '',\n    {\n      // The Component to be rendered on this route.\n      component: RouteComponent;\n\n      // The Parsers for the parameters in this route.\n      params: Record\u003cstring, ParamParser\u003cany\u003e\u003e;\n\n      // A middleware for this route\n      middleware?: RouteMiddleware;\n\n      // Global options for this route\n      options?: Partial\u003cRO\u003e;\n\n      // Wether or not to include this routes child routes in a RouterSwitch  - Defaults to true\n      includeChildren?: boolean;\n    }\n  ),\n});\n```\n\n### Route Template and Parameters\n\nEvery route **requires** a component to be defined and for every parameter you define you are **required** to define a parser.\n\n#### Basic parameters\n\nBasic parameters are defined with a colon in front of them.\n\n```tsx\nconst router = Route(route =\u003e ({\n  test: route('test/:id', {\n    component: TestPage,\n    params: {\n      id: intParser,\n    },\n  }),\n}));\n```\n\n#### Optional parameters\n\nIf you want a parameter to be optional you can add a question mark behind it. Optional parameters still **require** a parser to be defined.\n\n```tsx\nconst router = Route(route =\u003e ({\n  test: route('test/:id?', {\n    component: TestPage,\n    params: {\n      id: intParser,\n    },\n  }),\n}));\n```\n\n#### Query parameters\n\nA query parameter has an ampersand in front of it, they can be chained and also be made optional with a question mark.\n\n```tsx\nconst router = Route(route =\u003e ({\n  test: route('test/:id?\u0026:filter\u0026:page?', {\n    component: TestPage,\n    params: {\n      id: intParser,\n      page: intParser,\n      filter: stringParser,\n    },\n  }),\n}));\n```\n\n#### Child Routes\n\nChild routes can be defined with the third argument of the route function - Another route function!\n\n```tsx\nconst router = Route(route =\u003e ({\n  test: route(\n    'test/:id?\u0026:filter\u0026:page?',\n    {\n      component: TestPage,\n      params: {\n        id: intParser,\n        page: intParser,\n        filter: stringParser,\n      },\n    },\n    route =\u003e ({\n      child: route('test'),\n    })\n  ),\n}));\n```\n\n## Parameter Parsers\n\nEvery parameter has a parser which makes [useRouteParams](#userouteparams) possible.\n\n### Available Parsers\n\nThe following are self explanatory:\n\n- stringParser\n- floatParser\n- intParser\n- dateParser\n- booleanParser\n-\n\nBut there is also the **stringListParser** used like this:\n\n```tsx\n// Probably defined in your Page file\nconst testTabs = ['overview', 'statistics', 'comments'] as const;\n\nconst router = Route(route =\u003e ({\n  test: route('test\u0026:tab', {\n    component: TestPage,\n    params: {\n      tab: stringListParser(testTabs),\n    },\n  }),\n}));\n```\n\nWhich will result in your parameter being one of the tabs.\n\n### Your own parser\n\nThe general interface for a ParamParser is:\n\n```tsx\nexport interface ParamParser\u003cT\u003e {\n  parse: (s: string) =\u003e T;\n  serialize: (x: T) =\u003e string;\n}\n```\n\nYou can implement your own kind of parser as an example the **intParser**:\n\n```tsx\nexport const intParser: ParamParser\u003cnumber\u003e = {\n  parse: s =\u003e parseInt(s),\n  serialize: x =\u003e x.toString(),\n};\n```\n\n## Hooks\n\nThere are a few complementary Hooks to make your life easier.\n\n### useRouteOptions\n\nThis is useful whenever you need those global route options of an [OptionsRouter](#optionsrouter). Since you define defaults in the Router those values will never be undefined and always return the correct values for your current route.\n\n```tsx\nconst options = { appBar: true };\nconst router = OptionsRouter(options, route =\u003e ({\n  home: route('', {\n    component: HomePage\n  }),\n  entry: route('entries/:id', {\n    component: EntryPage\n    params: {\n      id: intParser\n    }\n  })\n}));\n\nconst options = useRouteOptions(router);\n\n// or destructured\nconst { appBar } = useRouteOptions(router);\n```\n\n### useRouteParams\n\nThis is the way to go when you need those parameters of your Route. Let's say you have the Router from right above.\n\n```tsx\nexport const EntryPage = () =\u003e {\n  // id is statically typed to be a number\n  const { id } = useRouteParams(router.entry);\n\n  return \u003cdiv\u003eEntry {id}\u003c/div\u003e;\n};\n```\n\n### useRouteActive and useRoutesActive\n\nThis is the way to go when you need those parameters of your Route. Let's say you have the Router from right above.\n\n```tsx\nconst HighlightLink = (\n  props: React.PropsWithChildren\u003c{\n    to: { $: string };\n    isActive: boolean;\n  }\u003e\n) =\u003e {\n  const style: React.CSSProperties = { color: 'blue' };\n  const activeStyle: React.CSSProperties = { color: 'red' };\n\n  return (\n    \u003cLink to={props.to} style={props.isActive ? activeStyle : style}\u003e\n      {props.children}\n    \u003c/Link\u003e\n  );\n};\n\nexport const App = () =\u003e {\n  // Check if a single route is active\n  const active = useRouteActive(router.home);\n\n  // Check if multiple routes are active\n  const { home, entry } = useRoutesActive({\n    home: router.home,\n    entry: router.entry,\n  });\n\n  return (\n    \u003cul\u003e\n      \u003cli\u003e\n        \u003cHighlightLink isActive={home} to={router.home()}\u003e\n          Home\n        \u003c/HighlightLink\u003e\n      \u003c/li\u003e\n      \u003cli\u003e\n        \u003cHighlightLink isActive={entry} to={router.entry()}\u003e\n          Entry\n        \u003c/HighlightLink\u003e\n      \u003c/li\u003e\n    \u003c/ul\u003e\n  );\n};\n```\n\n## Components\n\n### RouterSwitch\n\nThis is what you would use instead of the `Switch` and `Route` from `react-router-dom`. You just give it your router and it automatically adds al the routes for you.\n\n```tsx\n\u003cRouterSwitch router={router} /\u003e\n```\n\n### Link\n\nThis is a simple wrapper Component for the `react-router-dom` Link.\n\n```tsx\n\u003cLink to={router.home()}\u003e\u003c/Link\u003e\n```\n\n### NavLink\n\nThis is a simple wrapper Component for the `react-router-dom` NavLink.\n\n```tsx\n\u003cNavLink to={router.home()}\u003e\u003c/NavLink\u003e\n```\n\n### Redirect\n\nThis is a simple wrapper Component for the `react-router-dom` Redirect.\n\n```tsx\n\u003cRedirect to={router.home()}\u003e\u003c/Redirect\u003e\n```\n\n### Route\n\nThis is a simple wrapper Component for the `react-router-dom` Route.\n\n```tsx\n\u003cRoute to={router.home()}\u003e\u003c/Route\u003e\n```\n\n## Roadmap\n\n- Optional defaults for optional parameters\n- Parsing parent params in a nicer way\n\n## Contributing\n\nAll contributions are welcome. Please open an issue about your request or bug fix before submitting a pull request.\n\n\u003c!-- ALL-CONTRIBUTORS-LIST:START - Do not remove or modify this section --\u003e\n\u003c!-- prettier-ignore-start --\u003e\n\u003c!-- markdownlint-disable --\u003e\n\u003ctable\u003e\n  \u003ctr\u003e\n    \u003ctd align=\"center\"\u003e\u003ca href=\"https://github.com/DevNico\"\u003e\u003cimg src=\"https://avatars.githubusercontent.com/u/24965872?v=3?s=100\" width=\"100px;\" alt=\"\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003eDevNico\u003c/b\u003e\u003c/sub\u003e\u003c/a\u003e\u003cbr /\u003e\u003ca href=\"https://github.com/innFactory/react-typesafe-routes/commits?author=DevNico\" title=\"Code\"\u003e💻\u003c/a\u003e \u003ca href=\"https://github.com/innFactory/react-typesafe-routes/commits?author=DevNico\" title=\"Documentation\"\u003e📖\u003c/a\u003e\u003c/td\u003e\n  \u003c/tr\u003e\n\u003c/table\u003e\n\u003c!-- markdownlint-restore --\u003e\n\u003c!-- prettier-ignore-end --\u003e\n\n\u003c!-- ALL-CONTRIBUTORS-LIST:END --\u003e\n\n## License\n\nThis project is licensed under the terms of the [MIT license](LICENSE).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Finnfactory%2Freact-typesafe-routes","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Finnfactory%2Freact-typesafe-routes","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Finnfactory%2Freact-typesafe-routes/lists"}