{"id":18064752,"url":"https://github.com/franciscop/react-plain-router","last_synced_at":"2025-04-11T18:10:44.567Z","repository":{"id":57143151,"uuid":"158692593","full_name":"franciscop/react-plain-router","owner":"franciscop","description":"🛣 A 2kb React router that works exactly as expected with native Javascript","archived":false,"fork":false,"pushed_at":"2020-09-21T03:52:33.000Z","size":112,"stargazers_count":117,"open_issues_count":0,"forks_count":1,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-04-03T01:35:43.260Z","etag":null,"topics":["javascript","react","router","tiny","vanilla"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","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/franciscop.png","metadata":{"files":{"readme":"readme.md","changelog":"changelog.md","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":"2018-11-22T11:57:52.000Z","updated_at":"2024-11-19T04:05:35.000Z","dependencies_parsed_at":"2022-09-05T18:41:31.323Z","dependency_job_id":null,"html_url":"https://github.com/franciscop/react-plain-router","commit_stats":null,"previous_names":["franciscop/router"],"tags_count":6,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/franciscop%2Freact-plain-router","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/franciscop%2Freact-plain-router/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/franciscop%2Freact-plain-router/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/franciscop%2Freact-plain-router/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/franciscop","download_url":"https://codeload.github.com/franciscop/react-plain-router/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248456365,"owners_count":21106603,"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":["javascript","react","router","tiny","vanilla"],"created_at":"2024-10-31T06:07:28.842Z","updated_at":"2025-04-11T18:10:44.546Z","avatar_url":"https://github.com/franciscop.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# React Plain Router [![npm install react-plain-router](https://img.shields.io/badge/npm%20install-react--plain--router-blue.svg)](https://www.npmjs.com/package/react-plain-router) [![gzip size](https://img.badgesize.io/franciscop/router/master/router.min.js.svg?compression=gzip)](https://github.com/franciscop/router/blob/master/router.min.js)\n\nA tiny React router that works exactly as expected with native Javascript:\n\n```js\n// App.js\nimport React from 'react';\nimport router from 'react-plain-router';\n\n// Wrap it and you can use normal anchor links\nexport default router(({ path, query, hash }) =\u003e (\n  \u003cdiv\u003e\n    \u003cnav\u003e\n      \u003ca href=\"/\"\u003eGo Home\u003c/a\u003e\n      \u003ca href=\"/about\"\u003eAbout us\u003c/a\u003e\n    \u003c/nav\u003e\n\n    {path === '/' \u0026\u0026 \u003cdiv\u003eHello world!\u003c/div\u003e}\n    {path === '/about' \u0026\u0026 \u003cdiv\u003eAbout me\u003c/div\u003e}\n  \u003c/div\u003e\n));\n```\n\nYou have to wrap your app with `router()` and then both `\u003ca\u003e` links and `window.history.pushState()` will work as expected. It will trigger a re-render when any of these properties change: `path`, `query` or `hash`.\n\nIf you have parameters or complex routes you can combine it with my other library [`pagex`](https://github.com/franciscop/pagex) for a cleaner syntax:\n\n```js\nimport router from 'react-plain-router';\nimport page from 'pagex';\nexport default router(() =\u003e (\n  \u003cdiv\u003e\n    {page('/', () =\u003e \u003cdiv\u003eHello world!\u003c/div\u003e)}\n    {page('/users', () =\u003e \u003cul\u003e...\u003c/ul\u003e)}\n    {page('/users/:id', id =\u003e \u003cUser id={id} /\u003e)}\n  \u003c/div\u003e\n));\n```\n\nIf a link has the attribute `target`, then this library will ignore it and let the browser handle it. This is very useful for `target=\"_blank\"`, so that the link is opened in a new tab as usual. But it will also work with `target=\"_self\"`, where the router will ignore the link and thus perform a full-refresh.\n\nAn event named `navigation` will be triggered on `window` every time there's a re-render. You can see the parts with `e.detail`, which is very useful for debugging:\n\n```js\n// When loading /greetings?hello=world#nice\nwindow.addEventListener('navigation', e =\u003e {\n  console.log('NAVIGATION', e.detail);\n  // {\n  //   path: '/greetings',\n  //   query: { hello: 'world' },\n  //   hash: 'nice'\n  // }\n});\n```\n\n**Internally**, it works by using the bubbling events at the document level and then handling any link click. `window.location` becomes the source of truth instead of keeping an context or global store, which makes it more reliable to interact with native Javascript or http events.\n\n## router(cb)\n\nThis [HOC](https://reactjs.org/docs/higher-order-components.html) function accepts a callback, which will be passed an arguments with the props from above and these 3 extra props:\n\n- `path`, `pathname` (String): the current url path, similar to the native `pathname`. Example: for `/greetings` it will be `'/greetings'`. An empty URL would be `'/'`.\n- `query` (Object | false): an object with key:values for the query in the url. Example: for `/greeting?hello=world` it will be `{ hello: 'world' }`.\n- `hash` (String | false): the hash value without the `#`. Example: for `/hello#there` it will be `'there'`.\n\nA fully qualified url will parse as this:\n\n```js\n// /greetings?hello=world#nice\nrouter(({ path, query, hash, ...props }) =\u003e {\n  expect(path).toBe('/greetings');\n  expect(query).toEqual({ hello: 'world' });\n  expect(hash).toBe('nice');\n});\n```\n\n\n\n\n## Example: navigation bar\n\nWe can define our navigation in a different component. `\u003ca\u003e` are native so they will work cross-components:\n\n```js\n// Nav.js\nexport default () =\u003e (\n  \u003cnav\u003e\n    \u003ca href=\"/\"\u003eHome\u003c/a\u003e\n    \u003ca href=\"/about\"\u003eAbout\u003c/a\u003e\n    \u003ca href=\"https://google.com/\"\u003eGo to Google\u003c/a\u003e\n    \u003ca href=\"/terms\" target=\"_blank\"\u003eTerms and Conditions\u003c/a\u003e\n  \u003c/nav\u003e\n);\n```\n\nThen you can toggle the different pages in the main App.js:\n\n```js\n// App.js\nimport router from 'react-plain-router';\nimport Nav from './Nav';\n\nexport default router(({ path }) =\u003e (\n  \u003cdiv\u003e\n    \u003cNav /\u003e\n    {path === '/' \u0026\u0026 \u003cdiv\u003eHomepage\u003c/div\u003e}\n    {path === '/about' \u0026\u0026 \u003cdiv\u003eAbout us\u003c/div\u003e}\n  \u003c/div\u003e\n));\n```\n\nThe Google link will open Google, and the Terms and Conditions link will open a new tab. Everything works as expected, in the same way native html works.\n\n\n## Example: scroll to top on any navigation\n\nAdd an event listener to the navigation event:\n\n```js\nwindow.addEventListener('navigation', e =\u003e {\n  window.scrollTo(0, 0);\n});\n```\n\n\n\n## Example: simulating the `\u003cLink\u003e`\n\nJust an example of how easy it is to work with `react-plain-router`, let's see how to simulate the component that the library `react-router-dom` defines with this library\n\n```js\n// components/Link.js\nexport default ({ to, ...props }) =\u003e \u003ca href={to} {...props} /\u003e;\n```\n\nThen to use our newly defined component, we can import it and use it:\n\n```js\n// Home.js\nimport router from 'react-plain-router';\nimport Link from './components/Link';\n\nexport default router(() =\u003e (\n  \u003cLink to=\"/about\"\u003eAbout us\u003c/Link\u003e\n));\n```\n\nBut you can just use native links:\n\n```js\n// Home.js\nimport router from 'react-plain-router';\n\nexport default router(() =\u003e (\n  \u003ca href=\"/about\"\u003eAbout us\u003c/a\u003e\n));\n```\n\n\n## Example: manual navigation\n\nTo trigger manual navigation you can use the native `history.pushState()` as [explained in the amazing Mozilla Developer Network](https://developer.mozilla.org/en-US/docs/Web/API/History_API):\n\n```js\n// src/actions/login\nexport default id =\u003e async dispatch =\u003e {\n  const payload = await ky(`/api/users/${id}`).json();\n  dispatch({ type: 'USER_DATA', payload });\n  window.history.pushState({}, 'Dashboard', `/dashboard`);\n};\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffranciscop%2Freact-plain-router","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffranciscop%2Freact-plain-router","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffranciscop%2Freact-plain-router/lists"}