{"id":13683700,"url":"https://github.com/hanakla/froute","last_synced_at":"2025-04-30T13:31:45.705Z","repository":{"id":36963015,"uuid":"290166376","full_name":"hanakla/froute","owner":"hanakla","description":"Type safe and flexible router for React","archived":false,"fork":false,"pushed_at":"2024-10-14T07:46:41.000Z","size":3645,"stargazers_count":35,"open_issues_count":11,"forks_count":0,"subscribers_count":0,"default_branch":"dev","last_synced_at":"2025-04-24T09:51:49.015Z","etag":null,"topics":["react","react-router","router"],"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/hanakla.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":"2020-08-25T09:01:48.000Z","updated_at":"2024-12-27T16:00:02.000Z","dependencies_parsed_at":"2024-01-14T17:16:41.019Z","dependency_job_id":"c2f23c57-eecf-4b44-bd8a-08e9aed8693f","html_url":"https://github.com/hanakla/froute","commit_stats":{"total_commits":251,"total_committers":4,"mean_commits":62.75,"dds":0.4820717131474104,"last_synced_commit":"b6bd92f356185a7267fcd32ad1a54e1a93fdb555"},"previous_names":["hanakla/froute","fleur-js/froute"],"tags_count":5,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hanakla%2Ffroute","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hanakla%2Ffroute/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hanakla%2Ffroute/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hanakla%2Ffroute/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/hanakla","download_url":"https://codeload.github.com/hanakla/froute/tar.gz/refs/heads/dev","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":250887317,"owners_count":21503028,"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","router"],"created_at":"2024-08-02T13:02:24.331Z","updated_at":"2025-04-30T13:31:43.620Z","avatar_url":"https://github.com/hanakla.png","language":"TypeScript","funding_links":[],"categories":["TypeScript"],"sub_categories":[],"readme":"[npm-url]: https://www.npmjs.com/package/@fleur/froute\n[ci-image-url]: https://img.shields.io/github/workflow/status/fleur-js/froute/CI?logo=github\u0026style=flat-square\n[version-image-url]: https://img.shields.io/npm/v/@fleur/froute?style=flat-square\n[license-url]: https://opensource.org/licenses/MIT\n[license-image]: https://img.shields.io/npm/l/@fleur/froute.svg?style=flat-square\n[downloads-image]: https://img.shields.io/npm/dw/@fleur/froute.svg?style=flat-square\u0026logo=npm\n[bundlephobia-url]: https://bundlephobia.com/result?p=@fleur/froute@2.0.1\n[bundlephobia-image]: https://img.shields.io/bundlephobia/minzip/@fleur/froute?style=flat-square\n\n![CI][ci-image-url] [![latest][version-image-url]][npm-url] [![BundleSize][bundlephobia-image]][bundlephobia-url] [![License][license-image]][license-url] [![npm][downloads-image]][npm-url]\n\n# Froute\n\n[CHANGELOG](./pkgs/froute/CHANGELOG.md)\n\nFramework independent Router for React.  \nCan use with both Fleur / Redux (redux-thunk).\n\nWith provides Next.js subset `useRouter`\n\n```\nyarn add @fleur/froute\n```\n\n- [Features](#features)\n- [API Overview](#api-overview)\n  - [Hooks](#hooks)\n  - [Components](#components)\n- [Example](#example)\n- [Next.js compat status](#nextjs-compat-status)\n  - [How to type-safe useRoute](#how-to-type-safe-useroute)\n\n\n## Features\n\nSee all examples in [this spec](https://github.com/fleur-js/froute/blob/master/src/index.spec.tsx) or [examples](https://github.com/fleur-js/froute/tree/master/examples)\n\n- Library independent\n  - Works with Redux and Fleur\n- Next.js's Router subset compatiblity (`useRouter`, `withRouter`)\n- Supports dynamic import without any code transformer\n- Supports Sever Side Rendering\n  - Supports preload\n  - `ResponseCode` and `Redirect` component\n- Custom route resolution (for i18n support)\n- URL Builder\n\n## API Overview\n\n### Hooks\n\n- `useRouter` - **Next.js subset compat hooks**\n  - `withRouter` available\n- `useFrouteRouter` - `useRouter` superset (not compatible to Next.js's `useRouter`)\n- `useRouteComponent`\n- `useBeforeRouteChange(listener: () =\u003e Promise\u003cboolean | void\u003e | boolean | void)`\n  - It can prevent routing returns `Promise\u003cfalse\u003e | false`\n\n\u003cdetails\u003e\n\u003csummary\u003eDeprecated APIs\u003c/summary\u003e\n\nThe following hooks are deprecated. These features are available from `useFrouteRouter`.\n\n- `useParams`\n- `useLocation`\n- `useNavigation`\n- `useUrlBuilder`\n\u003c/details\u003e\n\n### Components\n\n- `\u003cLink href={string} /\u003e`\n- `\u003cFrouteLink to={routeDef} params={object} query={object} /\u003e` - Type-safe routing\n- `\u003cResponseCode status={number} /\u003e`\n- `\u003cRedirect url={string} status={number = 302}`\n\n## Getting started\n\nRoute definition:\n```tsx\nexport const routes = {\n  index: routeOf('/').action({\n    component: () =\u003e import('./pages/index'),\n  }),\n  user: routeOf('/users/:userId').action({\n    component: () =\u003e import('./pages/user'),\n    preload: (store: Store, params /* =\u003e inferred to { userId: string } */) =\u003e\n      Promise.all([ store.dispatch(fetchUser(param.userId)) ]),\n  })\n}\n```\n\nApp:\n```tsx\nimport { useRouteComponent, ResponseCode } from '@fleur/froute'\n\nexport const App = () =\u003e {\n  const { PageComponent } = useRouteComponent()\n\n  return (\n    \u003cdiv\u003e\n      {PageComponent ? (\n        \u003cPageComponent /\u003e \n      ) : (\n        \u003cResponseCode status={404}\u003e\n          \u003cNotFound /\u003e\n        \u003c/ResponseCode\u003e\n      )}\n    \u003c/div\u003e\n  )\n}\n```\n\nUser.tsx:\n```tsx\nimport { useRouter, buildPath } from '@fleur/froute'\nimport { routes, ResponseCode, Redirect } from './routes'\n\nexport default () =\u003e {\n  const { query: { userId } } = useRouter()\n  const user = useSelector(getUser(userId))\n\n  if (!user) {\n    return (\n      \u003cResponseCode status={404}\u003e\n        \u003cNotFound /\u003e\n      \u003c/ResponseCode\u003e\n    )\n  }\n\n  if (user.suspended) {\n    return (\n      \u003cRedirect status={301} url='/'\u003e\n        This account is suspended.\n      \u003c/Redirect\u003e\n    )\n  }\n  \n  return (\n    \u003cdiv\u003e\n      Hello, {user.name}!\n      \u003cbr /\u003e\n      \u003cLink href={buildPath(routes.user, { userId: '2' })}\u003e\n        Show latest update friend\n      \u003c/Link\u003e\n    \u003c/div\u003e\n  )\n}\n```\n\n\nServer side:\n```tsx\nimport { createRouter } from '@fleur/froute'\nimport { routes } from './routes'\n\nserver.get(\"*\", async (req, res, next) =\u003e {\n  const router = createRouter(routes, {\n    preloadContext: store\n  })\n\n  await router.navigate(req.url)\n  await context.preloadCurrent();\n\n  const content = ReactDOM.renderToString(\n    \u003cFrouteContext router={router}\u003e\n      \u003cApp /\u003e\n    \u003c/FrouteContext\u003e\n  )\n\n  // Handling redirect\n  if (router.redirectTo) {\n    res.redirect(router.statusCode, router.redirectTo)\n  } else{\n    res.status(router.statusCode)\n  }\n  \n  const stream = ReactDOM.renderToNodeStream(\n    \u003cHtml\u003e\n      {content}\n    \u003c/Html\u003e\n  ).pipe(res)\n\n  router.dispose()\n})\n```\n\nClient side:\n```tsx\nimport { createRouter, FrouteContext } from '@fleur/froute'\n\ndomready(async () =\u003e {\n  const router = createRouter(routes, {\n    preloadContext: store,\n  });\n\n  await router.navigate(location.href)\n  await router.preloadCurrent({ onlyComponentPreload: true })\n\n  ReactDOM.render((\n      \u003cFrouteContext router={router}\u003e\n        \u003cApp /\u003e\n      \u003c/FrouteContext\u003e\n    ),\n    document.getElementById('root')\n  )\n})\n```\n\n## Next.js compat status\n\n- Compat API via `useRouter` or `withRouter`\n  - Compatible features\n    - `query`, `push()`, `replace()`, `prefetch()`, `back()`, `reload()`\n    - `pathname` is provided, but Froute's pathname is not adjust to file system route.\n  - Any type check not provided from Next.js (Froute is provided, it's compat breaking)\n- Next.js specific functions not supported likes `asPath`, `isFallback`, `basePath`, `locale`, `locales` and `defaultLocale`\n  - `\u003cLink /\u003e` only href props compatible but behaviour in-compatible.\n    - Froute's Link has `\u003ca /\u003e` element. Next.js is not.\n    - `as`, `passHref`, `prefetch`, `replace`, `scroll`, `shallow` is not supported currently.\n  - `pathname` is return current `location.pathname`, not adjust to component file path base pathname.\n  - `router.push()`, `router.replace()`\n    - URL Object is does not support currentry\n    - `as` argument is not supported\n  - `router.beforePopState` is not supported\n    - Use `useBeforeRouteChange()` hooks instead\n  - `router.events`\n    - Partially supported: `routeChangeStart`, `routeChangeComplete`, `routeChangeError`\n      - Only `url` or `err` arguments.\n      - Not implemented: `err.cancelled` and `{ shallow }` flag.\n    - Not implemented: `beforeHistoryChange`, `hashChangeStart`, `hashChangeComplete`\n \n \n### Why froute provides Next.js compat hooks?\n\nIt aims to migrate to Next.js from react-router or another router.\n\nFroute's `useRouter` aims to provide a `useRouter` that is partially compatible with the Next.js `useRouter`, thereby guaranteeing an intermediate step in the migration of existing React Router-based applications to Next.js.\n\n\n### How to type-safe useRoute\n\nUse this snippet in your app.\n(It's breaking to Type-level API compatibility from Next.js)\n\n```tsx\n// Copy it in-your-app/useRouter.ts\nimport { useRouter as useNextCompatRouter } from '@fleur/froute'\nexport const useRouter: UseRouter = useNextCompatRouter\n```\n\nUsage:\n\n```tsx\n// Route definition\nconst routes = {\n  users: routeOf('/users/:id'),\n}\n\n// Typeing to `Routes`, it's free from circular dependency\nexport type Routes = typeof routes\n\n// Component\nimport { useRouter } from './useRouter'\nimport { Routes } from './your-routes'\n\nconst Users = () =\u003e {\n  const router = useRouter\u003ctypeof Routes['users']\u003e()\n  router.query.id // It infering to `string`.\n}\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhanakla%2Ffroute","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fhanakla%2Ffroute","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhanakla%2Ffroute/lists"}