{"id":15551305,"url":"https://github.com/franciscop/crossroad","last_synced_at":"2025-02-28T05:14:06.008Z","repository":{"id":59360231,"uuid":"395357696","full_name":"franciscop/crossroad","owner":"franciscop","description":"🛣 A React library to handle navigation in your WebApp. Built with simple components and React Hooks so your code is cleaner.","archived":false,"fork":false,"pushed_at":"2024-08-28T00:31:33.000Z","size":211,"stargazers_count":49,"open_issues_count":0,"forks_count":1,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-02-14T03:07:47.391Z","etag":null,"topics":["browser","javascript","react","react-router","router","routing","ssr"],"latest_commit_sha":null,"homepage":"https://crossroad.page","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":null,"contributing":null,"funding":".github/FUNDING.yml","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},"funding":{"custom":"https://www.paypal.me/franciscopresencia/19"}},"created_at":"2021-08-12T15:06:10.000Z","updated_at":"2025-01-26T13:20:56.000Z","dependencies_parsed_at":"2024-08-28T01:42:19.921Z","dependency_job_id":"a6c90534-6fbf-4690-886c-adcf02caff8e","html_url":"https://github.com/franciscop/crossroad","commit_stats":{"total_commits":158,"total_committers":2,"mean_commits":79.0,"dds":0.05696202531645567,"last_synced_commit":"97c57c258874cca3679bb33dbfa6a34934f587ab"},"previous_names":[],"tags_count":53,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/franciscop%2Fcrossroad","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/franciscop%2Fcrossroad/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/franciscop%2Fcrossroad/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/franciscop%2Fcrossroad/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/franciscop","download_url":"https://codeload.github.com/franciscop/crossroad/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":239952613,"owners_count":19723923,"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":["browser","javascript","react","react-router","router","routing","ssr"],"created_at":"2024-10-02T14:04:07.206Z","updated_at":"2025-02-21T04:06:42.303Z","avatar_url":"https://github.com/franciscop.png","language":"JavaScript","funding_links":["https://www.paypal.me/franciscopresencia/19"],"categories":[],"sub_categories":[],"readme":"# Crossroad [![npm install crossroad](https://img.shields.io/badge/npm%20install-crossroad-blue.svg \"install badge\")](https://www.npmjs.com/package/crossroad) [![test badge](https://github.com/franciscop/crossroad/workflows/tests/badge.svg \"test badge\")](https://github.com/franciscop/crossroad/blob/master/.github/workflows/tests.yml) [![gzip size](https://img.badgesize.io/franciscop/crossroad/master/index.min.js.svg?compression=gzip \"gzip badge\")](https://github.com/franciscop/crossroad/blob/master/index.min.js)\n\nA React library to handle navigation in your WebApp. Built with simple components and React Hooks so you write cleaner code:\n\n- `\u003cRouter\u003e`, `\u003cSwitch\u003e` and `\u003cRoute\u003e` inspired by React Router so it's easy to get started.\n- Very useful hooks like [`useUrl`](#useurl), [`useQuery`](#usequery), etc. Follow [the rules of hooks](https://reactjs.org/docs/hooks-rules.html).\n- Links are plain `\u003ca\u003e` instead of custom components. [Read more](#a).\n- The `\u003cRoute\u003e` path is `exact` by default and can match query parameters.\n- It's [just ~1.5kb](https://bundlephobia.com/package/crossroad) (min+gzip) instead of the 17kb of React Router(+Dom).\n- Add `scrollUp` to `\u003cRouter\u003e` o `\u003cRoute\u003e` to automatically scroll up on a route change.\n\n[**🔗 Demo on CodeSandbox**](https://codesandbox.io/s/recursing-wozniak-uftyo?file=/src/App.js)\n\n```js\n// App.js\nimport Router, { Switch, Route } from \"crossroad\";\n\nexport default function App() {\n  return (\n    \u003cRouter\u003e\n      \u003cnav\u003e\n        \u003ca href=\"/\"\u003eHome\u003c/a\u003e\n        \u003ca href=\"/users\"\u003eUsers\u003c/a\u003e\n        ...\n      \u003c/nav\u003e\n      \u003cSwitch redirect=\"/\"\u003e\n        \u003cRoute path=\"/\" component={Home} /\u003e\n        \u003cRoute path=\"/users\" component={Users} /\u003e\n        \u003cRoute path=\"/users/:id\" component={Profile} /\u003e\n      \u003c/Switch\u003e\n    \u003c/Router\u003e\n  );\n}\n```\n\n## Getting Started\n\nCreate a React project (`npx create-react-app demo`) and install Crossroad:\n\n```js\nnpm i crossroad\n```\n\nThen import it on your App.js and define some routes:\n\n```js\nimport Router, { Switch, Route } from \"crossroad\";\n\nexport default function App() {\n  return (\n    \u003cRouter\u003e\n      \u003cSwitch\u003e\n        \u003cRoute path=\"/\" component={Home} /\u003e\n        \u003cRoute path=\"/users/:id\" component={Profile} /\u003e\n      \u003c/Switch\u003e\n    \u003c/Router\u003e\n  );\n}\n```\n\nThen let's add some navigation and the actual pages:\n\n```js\nimport Router, { Switch, Route } from \"crossroad\";\n\nconst Home = () =\u003e \u003cmain\u003eHome Page\u003c/main\u003e;\nconst Profile = ({ id }) =\u003e \u003cmain\u003eHello {id.toUpperCase()}\u003c/main\u003e;\n\nexport default function App() {\n  return (\n    \u003cRouter\u003e\n      \u003cnav\u003e\n        \u003ca href=\"/\"\u003eHome\u003c/a\u003e\n        \u003ca href=\"/users/a\"\u003eUser A\u003c/a\u003e\n        \u003ca href=\"/users/b\"\u003eUser B\u003c/a\u003e\n      \u003c/nav\u003e\n      \u003cSwitch\u003e\n        \u003cRoute path=\"/\" component={Home} /\u003e\n        \u003cRoute path=\"/users/:id\" component={Profile} /\u003e\n      \u003c/Switch\u003e\n    \u003c/Router\u003e\n  );\n}\n```\n\nNow you can start your project and test it by visiting `http://localhost:3000/` and `http://localhost:3000/login`:\n\n```bash\nnpm start\n```\n\nSee the more complete working example [in this CodeSandbox](https://codesandbox.io/s/recursing-wozniak-uftyo?file=/src/App.js).\n\n## API\n\nThe API is composed of these parts:\n\n- [`\u003cRouter /\u003e`](#router): the top-level component that should wrap your whole app.\n- [`\u003cSwitch /\u003e`](#switch): renders only the first child that matches the current url.\n- [`\u003cRoute /\u003e`](#route): filters whether the given component should be rendered or not for the current URL.\n- [`\u003ca /\u003e`](#a): a plain HTML link, use it to navigate between pages.\n- [`useUrl()`](#useurl): a hook that returns the current URL and a setter to update it.\n- [`usePath()`](#usepath): a hook that returns the current path and a setter to update it.\n- [`useQuery()`](#usequery): a hook that returns the current query and a setter to update it.\n- [`useHash()`](#usehash): a hook that returns the current hash and a setter to update it.\n- [`useParams()`](#useparams): a hook that extracts params form the current path.\n\n`Router` is the default export, `\u003ca\u003e` is not exported since it's just the plain link element, and everything else are named exports:\n\n```js\nimport Router, { Switch, Route, useUrl, usePath } from \"crossroad\";\n```\n\n### `\u003cRouter /\u003e`\n\nThe top-level component that has to wrap everything else. Internally it's used to handle clicks, history, etc. It's also the default export of the library:\n\n```js\n// App.js\nimport Router from \"crossroad\";\n\nexport default function App() {\n  return \u003cRouter\u003e... Your normal App code ...\u003c/Router\u003e;\n}\n```\n\nAdd the prop `scrollUp` to automatically scroll up the browser window when _any_ route changes. In contrast, you could also add it only to a single or multiple `\u003cRoute\u003e`.\n\nAdd the prop `url` to simulate a fake URL instead of the current `window.location`, useful specially for testing.\n\nYou would normally setup this Router straight on your App, along things like [Statux](https://statux.dev/)'s or [Redux](https://redux.js.org/)'s Store, error handling, translations, etc.\n\nAn example for a simple app:\n\n```js\n// App.js\nimport Router, { Switch, Route } from \"crossroad\";\n\nimport Home from \"./pages/Home\";\nimport Dashboard from \"./pages/Dashboard\";\nimport Profile from \"./pages/Profile\";\n\nexport default function App() {\n  return (\n    \u003cRouter\u003e\n      \u003cSwitch\u003e\n        \u003cRoute path=\"/\" component={Home} /\u003e\n        \u003cRoute path=\"/dashboard\" component={Dashboard} /\u003e\n        \u003cRoute path=\"/:username\" component={Profile} /\u003e\n      \u003c/Switch\u003e\n    \u003c/Router\u003e\n  );\n}\n```\n\n### `\u003cSwitch /\u003e`\n\nA component that will only render the first of its children that matches the current URL. This is very useful to handle 404s, multiple routes matching, etc. For example, if you have a username system like `\"/:username\"` but want to have a help page, you can make it work easily with the switch:\n\n```js\n// In https://example.com/help, it'll render the Help component only\n\u003cSwitch\u003e\n  \u003cRoute path=\"/help\" component={Help} /\u003e\n  \u003cRoute path=\"/:username\" component={User} /\u003e\n\u003c/Switch\u003e\n```\n\nYou might want to redirect the user to a specific route (like `/notfound`) when none of the given routes matches the current URL. You can then use the attribute \"redirect\":\n\n```js\n\u003cSwitch redirect=\"/notfound\"\u003e\n  \u003cRoute path=\"/path1\" component={Comp1} /\u003e\n  \u003cRoute path=\"/path2\" component={Comp2} /\u003e\n  \u003cRoute component={NotFound} /\u003e\n\u003c/Switch\u003e\n```\n\nThe redirect parameter can be a plain string, an url-like object or a callback that returns any of the previous:\n\n```js\n\u003cSwitch redirect=\"/gohere?hello=world\"\u003e\u003c/Switch\u003e\n\u003cSwitch redirect={{ path: \"/gohere\", query: { hello: \"world\" } }}\u003e\u003c/Switch\u003e\n\u003cSwitch redirect={() =\u003e \"/gohere\"}\u003e\u003c/Switch\u003e\n\u003cSwitch redirect={url =\u003e ({ ...url, path: \"/gohere\" })}\u003e\u003c/Switch\u003e\n```\n\nOr to keep it in the current route, whatever it is, you can render a component with no path (no path === `*`):\n\n```js\n\u003cSwitch\u003e\n  \u003cRoute path=\"/path1\" component={Comp1} /\u003e\n  \u003cRoute path=\"/path2\" component={Comp2} /\u003e\n  \u003cRoute component={NotFound} /\u003e\n\u003c/Switch\u003e\n```\n\nThe `\u003cSwitch\u003e` component only accepts `\u003cRoute\u003e` as its children.\n\n### `\u003cRoute /\u003e`\n\nThis component defines a conditional path that, when strictly matched, renders the given component. Its props are:\n\n- `path`: the path to match to the current browser's URL. It can have parameters `/:id` and a wildcard at the end `*` to make it a partial route.\n- `component`: the component that will be rendered if the browser's URL matches the `path` parameter.\n- `render`: a function that will be called with the params if the browser's URL matches the `path` parameter.\n- `children`: the children to render if the browser's URL matches the `path` parameter.\n- `scrollUp`: automatically scroll up the browser window when this route/component/etc is matched.\n\nSo for example if the `path` prop is `\"/user\"` and you visit the page `\"/user\"`, then the component is rendered; it is ignored otherwise:\n\n```js\n// In https://example.com/\n\u003cRoute path=\"/\" component={Home} /\u003e // Rendered\n\u003cRoute path=\"/*\" component={Any} /\u003e  // Rendered\n\u003cRoute path=\"/user\" component={User} /\u003e  // Not rendered\n\u003cRoute path=\"/:page\" component={Page} /\u003e  // Not rendered\n\n// In https://example.com/user/\n\u003cRoute path=\"/\" component={Home} /\u003e // Not Rendered\n\u003cRoute path=\"/*\" component={Any} /\u003e  // Rendered\n\u003cRoute path=\"/user\" component={User} /\u003e  // Rendered\n\u003cRoute path=\"/:page\" component={Page} /\u003e  // Rendered\n```\n\nWhen matching a path with a parameter (a part of the url that starts with `:`) it will be passed as a prop straight to the children:\n\n```js\n// In https://example.com/user/abc\nconst User = ({ id }) =\u003e \u003cdiv\u003eHello {id}\u003c/div\u003e;\nconst UserList = () =\u003e \u003cdiv\u003eList here\u003c/div\u003e;\n\n\u003cRoute path=\"/user/:id\" component={User} /\u003e;\n// \u003cdiv\u003eHello abc\u003c/div\u003e\n\n\u003cRoute path=\"/user/:id\" render={({ id }) =\u003e \u003cUser id={id} /\u003e} /\u003e;\n// \u003cdiv\u003eHello abc\u003c/div\u003e\n\n// Avoid when you need the params, since they cannot be passed\n\u003cRoute path=\"/user/\"\u003e\n  \u003cUserList /\u003e\n\u003c/Route\u003e;\n// \u003cdiv\u003eList here\u003c/div\u003e\n```\n\n\u003e NOTE: the parameter is passed straight to the component instead of wrapped like in React Router.\n\nThe path can also include a wildcard `*`, in which case it will perform a partial match of everything before itself. It can only be at the end of the path:\n\n```js\n// In https://example.com/user/abc\n\n// All of these match the current route\n\u003cRoute path=\"*\" component={User} /\u003e\n\u003cRoute path=\"/*\" component={User} /\u003e\n\u003cRoute path=\"/user/*\" component={User} /\u003e\n\u003cRoute path=\"/user/abc/*\" component={User} /\u003e\n\u003cRoute path=\"/user/:id/*\" component={User} /\u003e\n```\n\n\u003e NOTE: in Crossroad the paths are exact by default, and with the wildcard you can make them partial matches. So the wildcard is the opposite of adding `exact` to React Router.\n\nIt can also match query parameters:\n\n```js\n// In /profile?page=settings\u0026filter=abc\n\n// All of these match the current route\n\u003cRoute path=\"/profile\" component={User} /\u003e\n\u003cRoute path=\"/profile?page\" component={User} /\u003e\n\u003cRoute path=\"/profile?page=settings\" component={User} /\u003e\n\u003cRoute path=\"/profile/*?page=settings\" component={User} /\u003e\n\u003cRoute path=\"/:id?page=settings\" component={User} /\u003e\n\u003cRoute path=\"/:id/*?page=settings\" component={User} /\u003e\n\n// These shall not match:\n\u003cRoute path=\"/?page\" component={User} /\u003e  // Wrong path\n\u003cRoute path=\"/profile?page2\" component={User} /\u003e  // Wrong key\n\u003cRoute path=\"/profile?page=options\" component={User} /\u003e  // Wrong value\n```\n\n### `\u003ca\u003e`\n\nLinks with Crossroad are just traditional plain `\u003ca\u003e`. You write the URL and a relative path, and Crossroad handles all the history, routing, etc:\n\n```js\nexport default () =\u003e (\n  \u003cnav\u003e\n    \u003ca href=\"/\"\u003eHome\u003c/a\u003e\n    \u003ca href=\"/users\"\u003eUsers\u003c/a\u003e\n    \u003ca href=\"/settings\"\u003eSettings\u003c/a\u003e\n  \u003c/nav\u003e\n);\n```\n\nAn important concept to understand is where links open, whether it's a react navigation or a browser page change:\n\n- `/`: plain paths will navigate within React\n- `/?abc=def`: queries, hashtags, etc. will also perform a navigation in React\n- `https://example.com/`: full URLs will trigger a browser page change\n- `target=\"_self\"`: will trigger a browser page change, in the same tab\n- `target=\"_blank\"`: will open a new tab\n\nSome examples:\n\n```js\n// In https://example.com/users/25\n\n// React navigation:\n\u003ca href=\"/\"\u003eHome\u003c/a\u003e\n\n// React navigation:\n\u003ca href=\"/users?filter=new\"\u003eNew users\u003c/a\u003e\n\n// Page refresh (since it's a full URL)\n\u003ca href=\"https://google.com/\"\u003eGoogle it\u003c/a\u003e\n\n// Page refresh (a full URL, even in the same domain)\n\u003ca href=\"https://example.com/\"\u003eHome\u003c/a\u003e\n\n// Page refresh (it has a target=\"_self\")\n\u003ca href=\"/update\" target=\"_self\"\u003eUpdate\u003c/a\u003e\n\n// New tab (it has a target=\"_blank\")\n\u003ca href=\"/terms-of-service\" target=\"_blank\"\u003eRead terms of service\u003c/a\u003e\n```\n\n### `useUrl()`\n\n\u003e NOTE: within Crossroad's and for lack of a better name, \"URL\" refers to the combination of path + search query + hash.\n\nRead and set the full URL:\n\n```js\nimport { useUrl } from \"crossroad\";\n\nexport default function Login() {\n  const [url, setUrl] = useUrl();\n\n  const login = async () =\u003e {\n    // ... do some stuff ...\n    setUrl(\"/welcome\");\n  };\n\n  return \u003cButton onClick={login}\u003eLogin\u003c/Button\u003e;\n}\n```\n\nThese are the structures of each:\n\n- `url`: an object with the properties, it's similar to the native URL:\n  - `url.path`: a string with the current pathname\n  - `url.query`: an object with the keys and values. Example: `{ q: 'hello' }`, `{ q: 'hello', s: 'world' }`.\n  - `url.hash`: the hashtag, without the \"#\"\n- `setUrl()`: a setter in the React Hooks style\n  - `setUrl(\"/newpath?search=hello\")`: a shortcut with the string\n  - `setUrl({ path: '/newpath' })`: set the path (and delete anything else if any)\n  - `setUrl({ path: '/newpath', query: { hello: 'world' } })`: update the path and query (and delete the hash if any)\n  - `setUrl(prev =\u003e ...)`: use the previous url (object)\n\n`useUrl()` is powerful enough for all of your needs, but you might still be interested in other hooks to simplify situations where you do e.g. heavy query manipulation with [`useQuery`](#usequery).\n\n#### url\n\nThe resulting `url` is an object containing each of the parts of the URL:\n\n```js\n// In /whatever?filter=hello#world\nconst [url, setUrl] = useUrl();\nconsole.log(url.path); // /whatever\nconsole.log(url.query); // { filter: hello }\nconsole.log(url.hash); // world\n```\n\nIt is memoized, so that if the url doesn't change then the object will remain the same. The same of course applies to the subelements like `url.path`. It will however change when the url changes, so you want to put it in your dependencies as usual:\n\n```js\n// You can put the whole thing if you want to listen to\n// ANY change on the url\nuseEffect(() =\u003e {\n  // ...\n}, [url]);\n\n// Or only a part of it. This is useful becase it WON'T trigger\n// when the query or hashtag change\nuseEffect(() =\u003e {\n  // ...\n}, [url.path]);\n```\n\n#### Setter\n\nThe setter can be invoked directly, or with a callback:\n\n```js\nconst [url, setUrl] = useUrl();\n\n// [Shorthand] Redirect to home with a hashtag\nsetUrl(\"/#firsttime\");\n\n// Same as above, but specifying the parts\nsetUrl({ path: \"/\", hash: \"firsttime\" });\n\n// Keep everything the same except the path\nsetUrl({ ...url, path: \"/\" });\n\n// Set a full search query\nsetUrl({ ...url, query: { search: \"hello\" } });\n\n// Modify only one query param\nsetUrl({ ...url, query: { ...url.query, safe: \"no\" } });\n```\n\nThe function `setUrl` is _always_ the same, so it doesn't matter whether you put it as a dependency or not. However the `path` can be updated and change, so you want to depend on it:\n\n```js\nconst [url, setUrl] = useUrl();\nuseEffect(() =\u003e {\n  if (url.path === \"/base\") {\n    setUrl(\"/base/deeper\");\n  }\n}, [url.path, setUrl]);\n```\n\nIf you update the url with the current url, it won't trigger a rerender. So the above can also be written as this, removing all dependencies:\n\n```js\nconst [url, setUrl] = useUrl();\nuseEffect(() =\u003e {\n  setUrl((old) =\u003e {\n    if (old.path === \"/base\") return \"/base/deeper\";\n    return old;\n  });\n}, []);\n```\n\n#### New history entry\n\nBy default `setUrl()` will create a new entry in the browser history. If you want to instead replace the current url you can pass a second parameter with `{ mode: 'replace' }`:\n\n```js\nsetUrl(\"/newurl\"); // Default: \"push\"\nsetUrl(\"/newurl\", { mode: \"replace\" });\n```\n\n- `push` (default): creates a new entry in the history. E.g. if you navigate `/a` =\u003e `/b` =(push)\u003e `/c` and then click on the back button, the browser will go back to `/b`. This is because `/b` and `/c` are both independent entries in your history.\n- `replace`: creates a new entry in the history. E.g. if you navigate `/a` =\u003e `/b` =(replace)\u003e `/c` and then click on the back button, it'll go back to `/a`. This is because `/c` is overwriting `/b`, instead of adding a new entry.\n\n### `usePath()`\n\nRead and set only the path(name) part of the URL:\n\n```js\nconst Login = () =\u003e {\n  const [path, setPath] = usePath();\n\n  const login = async () =\u003e {\n    // ...\n    setPath(\"/welcome\");\n  };\n\n  return \u003cButton onClick={login}\u003eLogin\u003c/Button\u003e;\n};\n```\n\nThe path is always a string equivalent to `window.location.pathname`. Why not use `window.location.pathname` then? Because usePath() is a hook that will trigger a re-render when the path changes!\n\n\u003e Note: `setPath` _only_ modifies the path(name) and keeps the search query and hash the same, so if you want to modify the full URL you should instead utilize `useUrl()` and `setUrl('/welcome')`\n\n#### Setter\n\nThe setter can be invoked directly, or with a callback:\n\n```js\nsetPath(\"/newpath\");\nsetPath((oldPath) =\u003e \"/newpath\");\n```\n\nThe function `setPath` is _always_ the same, so it doesn't matter whether you put it as a dependency or not. However the `path` can be updated, so you might want to put that:\n\n```js\nconst [path, setPath] = usePath();\nuseEffect(() =\u003e {\n  if (path === \"/base\") {\n    setPath(\"/base/deeper\");\n  }\n}, [path, setPath]);\n```\n\nIf you update the path with the current path, it won't trigger a rerender. So the above can also be written as this, removing all dependencies:\n\n```js\nconst [path, setPath] = usePath();\nuseEffect(() =\u003e {\n  setPath((old) =\u003e {\n    if (old === \"/base\") return \"/base/deeper\";\n    return old;\n  });\n}, []);\n```\n\n#### New history entry\n\nBy default `setPath()` will create a new entry in the browser history. If you want to instead replace the current url you can pass a second parameter with `{ mode: 'replace' }`:\n\n```js\nsetPath(\"/newpath\"); // Default: \"push\"\nsetPath(\"/newpath\", { mode: \"replace\" });\n```\n\n- `push` (default): creates a new entry in the history. E.g. if you navigate `/a` =\u003e `/b` =(push)\u003e `/c` and then click on the back button, the browser will go back to `/b`. This is because `/b` and `/b?q=c` are both independent entries in your history.\n- `replace`: creates a new entry in the history. E.g. if you navigate `/a` =\u003e `/b` =(replace)\u003e `/c` and then click on the back button, it'll go back to `/a`. This is because `/c` is overwriting `/b`, instead of adding a new entry.\n\n### `useQuery()`\n\nRead and set only the search query parameters from the URL:\n\n```js\nimport { useQuery } from \"crossroad\";\n\nexport default function SearchInput() {\n  // In /users?search=\n  const [query, setQuery] = useQuery();\n  // [{ search: \"\" }, fn]\n\n  // Goes to /users?search={value}\n  const onChange = (e) =\u003e setQuery({ search: e.target.value });\n\n  return \u003cinput value={query.search} onChange={onChange} /\u003e;\n}\n```\n\nIf you pass a key, it can read and modify that parameter while keeping the others the same. This is specially useful in e.g. a search form:\n\n```js\n// In /users?search=name\u0026filter=new\nconst [search, setSearch] = useQuery(\"search\");\n// 'name'\n\nsetSearch(\"myname\");\n// Goto /users?search=myname\u0026filter=new\n```\n\nWhen you update it, it will clean any parameter not passed, so make sure to pass the old ones if you want to keep them or a new object if you want to scrub them:\n\n```js\n// In /users?search=name\u0026filter=new\nconst [query, setQuery] = useQuery();\n\nsetQuery({ search: \"myname\" });\n// Goto /users?search=myname  (removes the filter)\n\nsetQuery({ ...query, search: \"myname\" });\n// Goto /users?search=myname\u0026filter=new\n\nsetQuery((prev) =\u003e ({ ...prev, search: \"myname\" }));\n// Goto /users?search=myname\u0026filter=new\n```\n\n`setQuery` only modifies the query string part of the URL, keeping the `path` and `hash` the same as they were previously.\n\nWhen you set a search query to `null` it will be removed from the URL. However, empty strings `\"\"`, zero `0` or boolean `false` are not removed. So if you want falsy values to also remove the parameter in the URL, please do this:\n\n```js\nconst [myname, setMyname] = useQuery(\"myname\");\n\n// ...\n\nsetMyname(newName || null);\n```\n\nIf you are using `react-query` and already have a bunch of `useQuery()` in your code and prefer to use other name, you can rename this method when importing it:\n\n```js\nimport { useQuery as useSearch } from 'crossroad';\n...\n```\n\n#### New history entry\n\nBy default `setQuery()` will create a new entry in the browser history. If you want to instead replace the current entry, so that the \"Back\" button goes to the previous page, you can pass a second parameter with `{ mode: 'replace' }`:\n\n```js\nsetQuery({ search: \"abc\" }); // Default: \"push\"\nsetQuery({ search: \"abc\" }, { mode: \"replace\" });\n```\n\n- `push` (default): creates a new entry in the history. E.g. if you navigate `/a` =\u003e `/b` =(push)\u003e `/b?q=c` and then click on the back button, the browser will go back to `/b`. This is because `/b` and `/b?q=c` are both independent entries in your history.\n- `replace`: creates a new entry in the history. E.g. if you navigate `/a` =\u003e `/b` =(replace)\u003e `/b?q=c` and then click on the back button, it'll go back to `/a`. This is because `/b?q=c` is overwriting `/b`, instead of adding a new entry.\n\n### `useHash()`\n\nRead and set only the hash part of the URL (without the `\"#\"`):\n\n```js\n// In /login#welcome\nconst [hash, setHash] = useHash();\n// welcome\n\nsetHash(\"bye\");\n// Goto /login#bye\n```\n\nBy default `setHash()` will create a new entry in the browser history. If you want to instead replace the current entry you can pass a second parameter with `{ mode: 'replace' }`:\n\n```js\nsetHash(\"newhash\", { mode: \"replace\" });\n```\n\nIf you want to remove the hash, pass a `null` or `undefined` to the setter.\n\n#### New history entry\n\nBy default `setHash()` will create a new entry in the browser history. If you want to instead replace the current entry, so that the \"Back\" button goes to the previous page, you can pass a second parameter with `{ mode: 'replace' }`:\n\n```js\nsetHash(\"newhash\"); // Default: \"push\"\nsetHash(\"newhash\", { mode: \"replace\" });\n```\n\n- `push` (default): creates a new entry in the history. E.g. if you navigate `/a` =\u003e `/b` =(push)\u003e `/b#c` and then click on the back button, the browser will go back to `/b`. This is because `/b` and `/b?q=c` are both independent entries in your history.\n- `replace`: creates a new entry in the history. E.g. if you navigate `/a` =\u003e `/b` =(replace)\u003e `/b#c` and then click on the back button, it'll go back to `/a`. This is because `/b#c` is overwriting `/b`, instead of adding a new entry.\n\n### `useParams()`\n\nParse the current URL against the given reference:\n\n```js\n// In /users/2\nconst params = useParams(\"/users/:id\");\n// { id: '2' }\n```\n\n\u003e Note: this returns a plain object, not a [value, setter] array\n\nIt's not this method responsibility to match the url, just to attempt to parse it, so if there's no good match it'll just return an empty object (use a `\u003cRoute /\u003e` for path matching):\n\n```js\n// In /pages/settings\nconst params = useParams(\"/users/:id\");\n// {}\n```\n\n## Examples\n\n### Static routes\n\nLet's see a traditional company website, where you have a homepage, some specific pages and a PDF:\n\n[**Codesandbox example**](https://codesandbox.io/s/loving-joana-jikne)\n\nhttps://user-images.githubusercontent.com/2801252/131257834-bfd9b6c6-f22e-46f2-9d06-8c14ac7f2708.mp4\n\n```js\n// App.js\nimport Router, { Switch, Route } from \"crossroad\";\n\nimport Nav from \"./Nav\";\nimport Pages from \"./Pages\";\n\nexport default function App() {\n  return (\n    \u003cRouter\u003e\n      \u003cNav /\u003e\n      \u003cSwitch redirect=\"/\"\u003e\n        \u003cRoute path=\"/\" component={Pages.Home} /\u003e\n        \u003cRoute path=\"/about\" component={Pages.AboutUs} /\u003e\n        \u003cRoute path=\"/product1\" component={Pages.MainProduct} /\u003e\n        \u003cRoute path=\"/product2\" component={Pages.AnotherProduct} /\u003e\n      \u003c/Switch\u003e\n    \u003c/Router\u003e\n  );\n}\n```\n\nNow that we have our routing, and for simplicity sake, let's say all of our navigation links are inside a `\u003cnav\u003e`:\n\n```js\nexport default function Nav() {\n  return (\n    \u003cnav\u003e\n      \u003ca href=\"/\"\u003eHome\u003c/a\u003e\n      \u003ca href=\"/about\"\u003eAbout Us\u003c/a\u003e\n      \u003ca href=\"/product1\"\u003eProduct 1\u003c/a\u003e\n      \u003ca href=\"/product2\"\u003eProduct 2\u003c/a\u003e\n      \u003ca href=\"/license.pdf\" target=\"_blank\"\u003e\n        License\n      \u003c/a\u003e\n    \u003c/nav\u003e\n  );\n}\n```\n\nThat's it, in the [Codesandbox](https://codesandbox.io/s/loving-joana-jikne) we added some filler for the pages, but that's the basic structure of how to make it work.\n\n### Vanity URLs\n\nThese refer to the websites where your username is straight after the domain, like Twitter (https://twitter.com/fpresencia). Of course Twitter has _other_ pages besides the username, so how can we emulate loading the page e.g. `/explore` in this case?\n\nThe best way is to first define the known, company pages and then use the wildcard for the usernames. This **must** be inside a `\u003cSwitch\u003e`, otherwise multiple will be rendered:\n\n```js\n\u003cSwitch\u003e\n  \u003c!-- Company pages --\u003e\n  \u003cRoute path=\"/\" component={Home} /\u003e\n  \u003cRoute path=\"/home\" component={Home} /\u003e\n  \u003cRoute path=\"/explore\" component={Explore} /\u003e\n\n  \u003c!-- Username page --\u003e\n  \u003cRoute path=\"/:username\" component={Profile} /\u003e\n\u003c/Switch\u003e\n```\n\nThis way we can handle the username inside Profile, and the other company-specific pages will load as expected. To work with the parameter, you can either use the props passed form the component or with the hook [useParams()](#useparams):\n\n```js\n// The parameters are passed straight to the component:\nfunction Profile({ username }) {\n  return \u003cdiv\u003eHello {username}\u003c/div\u003e;\n}\n\n// or\n\n// Use a hook to access the parameters:\nfunction Profile() {\n  const { username } = useParams(\"/:username\");\n  //\n  return \u003cdiv\u003eHello {username}\u003c/div\u003e;\n}\n\n// or\n\n// The path is defined as `/:username` already in \u003cRoute\u003e, we can reuse that:\nfunction Profile() {\n  const { username } = useParams();\n  //\n  return \u003cdiv\u003eHello {username}\u003c/div\u003e;\n}\n```\n\nIn the end of the day we recommend picking one style and following it. For simple applications we recommend the first one, where you receive the parameters straight in the props. For more complex applications, including those with deep nesting, we recommend the hook with the named parameter (explicit is more clear than implicit).\n\n### Search page\n\nThere are many ways to store the state to be able to visit later; localStorage, through API calls to the backend, cookies, etc. One place that people don't think often is the URL itself.\n\nThanks to `useQuery()`, it's trivial to use the search query for storing variables. Let's say you are looking for trips in a specific location, on a budget:\n\n[**Codesandbox demo**](https://codesandbox.io/s/festive-murdock-1ctv6?file=/src/SearchForm.js)\n\nhttps://user-images.githubusercontent.com/2801252/132189338-e09aa220-b773-43ed-803b-fa6c7449bf44.mp4\n\n```js\nimport { useQuery } from \"crossroad\";\n\nexport default function SearchForm() {\n  const [place, setPlace] = useQuery(\"place\");\n  const [max, setMax] = useQuery(\"max\");\n\n  return (\n    \u003cform\u003e\n      \u003cTextInput value={place} onChange={setPlace} ... /\u003e\n      \u003cNumberInput value={max} onChange={setMax} ... /\u003e\n    \u003c/form\u003e\n  );\n}\n```\n\nIn here we can see that we are treating the output of `useQuery` in the same way that we'd treat the output of `useState()`. This is on purpose and it makes things a lot easier for your application to work.\n\n### Query routing\n\nSome times you prefer the current page to be defined by the query, instead of by the pathname. This might be true for subpages, for tabs, or for other things depending on your app. With Crossroad it's easy to manage:\n\n[**Codesandbox**](https://codesandbox.io/s/white-moon-5q0hr)\n\nhttps://user-images.githubusercontent.com/2801252/132944863-3caf9399-d0c1-4cdc-86a0-dca1a6a4b4d1.mp4\n\n```js\n\u003cSwitch redirect=\"/?page=home\"\u003e\n  \u003cRoute path=\"/?page=home\" component={Tabs.Home} /\u003e\n  \u003cRoute path=\"/?page=product\" component={Tabs.Product} /\u003e\n  \u003cRoute path=\"/?page=about\" component={Tabs.About} /\u003e\n\u003c/Switch\u003e\n```\n\nWith the code above, it will match the given component when the path is exactly \"/\" and the query parameter is the given one. If no one is matched, then it'll redirect you to `/?page=home`, the main page.\n\nYou can also use this for subpages, say if you were in a Dashboard:\n\n```js\n\u003cSwitch redirect=\"/dashboard?tab=home\"\u003e\n  \u003cRoute path=\"/dashboard?tab=home\" component={Tabs.Home} /\u003e\n  \u003cRoute path=\"/dashboard?tab=product\" component={Tabs.Product} /\u003e\n  \u003cRoute path=\"/dashboard?tab=about\" component={Tabs.About} /\u003e\n\u003c/Switch\u003e\n```\n\n### Not found\n\nWe have already seen in different examples how to do simple redirects with a single `\u003cSwitch redirect=\"\"\u003e`, so now let's create a page for whenever the switch is not found:\n\n```js\n\u003cSwitch\u003e\n  \u003cRoute path=\"/\" component={Home} /\u003e\n  \u003cRoute path=\"/users\" component={Users} /\u003e\n\n  {/* Not found page */}\n  \u003cRoute component={NotFound} /\u003e\n\u003c/Switch\u003e\n```\n\nThis page will maintain the url in the browser, but render the NotFound component. Notice how we didn't write any `path=\"\"`, omitting the `path` is the same as writing it as `path=\"*\"`, which will catch everything.\n\nSo the way this Switch works here, it will try to match the URL against `\"/\"`, then against `\"/users\"`, and if it's none of those it'll match it against `\"*\"` (since that's always a match) and render the NotFound component.\n\nWe can also have different not found pages. Let's say we have a specific \"documentation page not found\" with helpful documentation links and a general one for the rest of the website, we can manage them this way then:\n\n```js\n\u003cSwitch\u003e\n  \u003cRoute path=\"/\" component={Home} /\u003e\n  \u003cRoute path=\"/docs/abc\" component={DocsAbc} /\u003e\n  \u003cRoute path=\"/docs/def\" component={DocsDef} /\u003e\n\n  {/* Not found page only for the docs */}\n  \u003cRoute path=\"/docs/*\" component={NotFoundDocs} /\u003e\n\n  {/* Not found page only for everything else */}\n  \u003cRoute component={NotFound} /\u003e\n\u003c/Switch\u003e\n```\n\nIn this case the order matters, because the generic NotFound will be matched with any route (since it's `\"*\"`), so we need to match first the docs that is not found and then, even if that fails (e.g. on the path `/hello`) we can render the generic NotFound component.\n\n### Github hosting\n\n\u003e NOTE: this is a bad idea for SEO, but if that doesn't matter much for you go ahead and host your webapp in Github Pages\n\nGithub pages is a bit particular in that as of this writing it does not allow for a generic redirect like most other static website servers, so we need to do a workaround with the `404.html` page.\n\nThis is because any of your visitors landing on `https://example.com/` will see the proper website (since that'll be directed to `docs/index.html`), but when the user lands on other paths like `https://example.com/info` it'll not find `docs/info.html` and thus render `404.html`.\n\nSo let's save the url and setup a redirect in `404.html`:\n\n```html\n\u003c!-- 404.html --\u003e\n\u003c!DOCTYPE html\u003e\n\u003chtml lang=\"en\"\u003e\n  \u003chead\u003e\n    \u003cmeta charset=\"UTF-8\" /\u003e\n    \u003cmeta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" /\u003e\n    \u003cmeta http-equiv=\"X-UA-Compatible\" content=\"ie=edge\" /\u003e\n    \u003ctitle\u003eRedirecting...\u003c/title\u003e\n  \u003c/head\u003e\n  \u003cbody\u003e\n    \u003cscript\u003e\n      const url = JSON.stringify(location.pathname + location.search);\n      localStorage.url = url;\n      location.replace(\"/\");\n    \u003c/script\u003e\n  \u003c/body\u003e\n\u003c/html\u003e\n```\n\nThen in your index.html, or in almost anywhere else, you can overwrite the URL:\n\n```js\nif (localStorage.url) {\n  history.replaceState({}, null, JSON.decode(localStorage.url));\n  delete localStorage.url;\n}\n```\n\n### Testing routes\n\nWhen testing a route, we can do it mainly in two different ways. The recommended one in general is that you pass a `url` prop straight into your `\u003cRouter\u003e` component, which will force the Router to behave like the browser is in that route.\n\nLet's see first a very simple App example, noting that for this case we are passing the `url` from App to Router:\n\n```js\n// App.js\nimport Router, { Switch, Route } from \"crossroad\";\n\n// Imagine these are your apps and components:\nconst Home = () =\u003e \u003cdiv\u003eHome\u003c/div\u003e;\nconst Users = () =\u003e \u003cdiv\u003eUsers\u003c/div\u003e;\nconst NotFound = () =\u003e \u003cdiv\u003eWebsite not found\u003c/div\u003e;\n\nexport default function App({ url }) {\n  return (\n    \u003cRouter url={url}\u003e\n      \u003cSwitch\u003e\n        \u003cRoute path=\"/\" component={Home} /\u003e\n        \u003cRoute path=\"/users\" component={Users} /\u003e\n        \u003cRoute component={NotFound} /\u003e\n      \u003c/Switch\u003e\n    \u003c/Router\u003e\n  );\n}\n```\n\nHow does it work? The `url` prop will be undefined when a user loads the app (since we **don't** add it to index.js), so it is only being written for testing. On the users' browser, since it's undefinde Crossroad will use `window.location.href` instead.\n\n```js\n// App.test.js\nimport React from \"react\";\nimport $ from \"react-test\";\n\nimport App from \"./App\";\n\ndescribe(\"use the url prop\", () =\u003e {\n  it(\"renders the home component on /\", () =\u003e {\n    const $home = $(\u003cApp url=\"/\" /\u003e);\n    expect($home.text()).toBe(\"Home\");\n  });\n\n  it(\"renders the user list on /users\", () =\u003e {\n    const $home = $(\u003cApp url=\"/users\" /\u003e);\n    expect($home.text()).toBe(\"Users\");\n  });\n\n  it(\"renders not found when in another route\", () =\u003e {\n    const $home = $(\u003cApp url=\"/bla\" /\u003e);\n    expect($home.text()).toContain(\"not found\");\n  });\n});\n```\n\nThis method is the simplest to get started, but some people don't like having to add code to the production website only for the testing environment. That's all fine, there's another way that is a bit harder to setup but it's also more accurate to the browser's real behavior.\n\n#### Mock window.location\n\nThe previous method has **a big limitation**: it doesn't allow you to navigate within your app for a test, since it's always forcing the same url. To avoid this and be able to test better the real-world behavior, use this method.\n\nWhen you are running Jest, it creates a fake `window` already, so you can plug into that to mock the behavior for the duration of the test. Doing it with a React component makes it even smoother:\n\n```js\n// Mock.js\nimport React, { useEffect } from \"react\";\n\nexport default function Mock({ url, children }) {\n  const href = \"http://localhost:3000\" + url;\n  const oldLocation = { value: window.location };\n  delete global.window.location;\n  Object.defineProperty(global.window, \"location\", {\n    value: new URL(href),\n    configurable: true,\n  });\n\n  // Undo the setup when the component unmounts\n  useEffect(() =\u003e {\n    return () =\u003e Object.defineProperty(window, \"location\", oldLocation);\n  }, []);\n  return \u003cdiv\u003e{children}\u003c/div\u003e;\n}\n```\n\nWith this Mock component, then you can wrap your normal application into working with routes:\n\n```js\nimport React from \"react\";\nimport $ from \"react-test\";\n\nimport App from \"./App\";\nimport Mock from \"./Mock\";\n\ndescribe(\"use the Mock component\", () =\u003e {\n  it(\"renders the home component on /\", () =\u003e {\n    const $home = $(\n      \u003cMock url=\"/\"\u003e\n        \u003cApp /\u003e\n      \u003c/Mock\u003e\n    );\n    expect($home.text()).toBe(\"Home\");\n  });\n\n  it(\"renders the user list on /users\", () =\u003e {\n    const $home = $(\n      \u003cMock url=\"/users\"\u003e\n        \u003cApp /\u003e\n      \u003c/Mock\u003e\n    );\n    expect($home.text()).toBe(\"Users\");\n  });\n\n  it(\"renders not found when in another route\", () =\u003e {\n    const $home = $(\n      \u003cMock url=\"/bla\"\u003e\n        \u003cApp /\u003e\n      \u003c/Mock\u003e\n    );\n    expect($home.text()).toContain(\"not found\");\n  });\n});\n```\n\n### Server Side Render\n\nCrossroad has been tested with these libraries/frameworks for SSR:\n\n- ✅ [Razzle](https://razzlejs.org/): it works adding a bit of config; Razzle bundles React Router Dom by default, so you need to install Crossroad, remove React Router Dom and add the code mentioned below.\n- ⚠️ [Next.js](https://nextjs.org/): it works, but is generally not needed since Next.js include its own router and file-based routing.\n- ❌ [Babel-Node](https://babeljs.io/docs/en/babel-node): BabelNode [doesn't support ECMAScript modules (ESM)](https://babeljs.io/docs/en/babel-node#es6-style-module-loading-may-not-function-as-expected), but you are **also** [not supposed to use `babel-node` for production anyway](https://babeljs.io/docs/en/babel-node#not-meant-for-production-use) so this is not a real framework for SSR.\n- Others? I couldn't find many other ways that people are running SSR that I could test.\n\nFor Razzle (based on [these docs FaQ](https://razzlejs.org/docs/customization#transpilation-of-external-modules)):\n\n```js\n// razzle.config.js\nmodule.exports = {\n  modifyWebpackOptions({ options: { webpackOptions } }) {\n    webpackOptions.notNodeExternalResMatch = (req) =\u003e /crossroad/.test(req);\n    webpackOptions.babelRule.include.push(/crossroad/);\n    return webpackOptions;\n  },\n};\n```\n\nWhen working on the server, and similar to [how we saw in testing](#testing-routes), we can overload the current url:\n\n```js\n// An express example\nconst App = ({ url }) =\u003e \u003cRouter url={url}\u003e...\u003c/Router\u003e;\n\napp.get(\"/users\", (req, res) =\u003e {\n  res.render(\u003cApp url=\"/users\" /\u003e);\n});\n\napp.get(\"/users/:id\", (req, res) =\u003e {\n  // {...} validate the `id` it here!\n\n  res.render(\u003cApp url={req.url} /\u003e);\n});\n```\n\n## React Router diff\n\nThis part of the documentation tries to explain in detail the differences between Crossroad and React Router (Dom). Crossroad goal is to build a modern Router API from scratch, removing the legacy code and using Hooks natively.\n\n### Intuitive API\n\nI've been using React Router for 3-4 years and I _still_ get wrong how to properly import it and have to try 2-3 combinations or reading the docs! I prefer to use intuitive tools that I can learn and get out of the way.\n\nSo this is a clear win, with Crossroad you import it like this:\n\n```js\nimport Router, { Switch, Route, ... } from 'crossroad';\n```\n\nWhile with React Router, guess the correct one:\n\n```js\nimport { Switch, Route, ... } from 'react-router';\nimport Router from 'react-router-dom';\n\n\nimport Router, { Switch, Route, ... } from 'react-router-dom';\n\nimport { Switch, Route, ... } from 'react-router';\nimport { BrowserRouter } from 'react-router-dom';\n```\n\n\u003e Tip: none of them are correct!\n\n### Remove imperative API\n\nWith React Router your component receives the props `history`. This is no longer needed with Crossroad; instead of handling the history details, we provide a hook `useUrl()` with the setter `setUrl()` where you can set the new URL straight away:\n\n```js\nimport { useUrl } from \"crossroad\";\n\nexport default function LoginButton() {\n  const [url, setUrl] = useUrl();\n  const login = async (e) =\u003e {\n    // ...\n    setUrl(\"/welcome\");\n  };\n  return \u003cbutton onClick={login}\u003eLogin\u003c/button\u003e;\n}\n```\n\nThe other hooks, like [`useQuery()`](usequery), behave in a similar way so you don't need to be concerned about the history API at all.\n\n### Useful Hooks\n\nI've seen in multiple codebases people end up creating a `useQuery()` hook wrapping `useLocation` and `useHistory` to work with query parameters. Fear no more, this and some other useful hooks are there already on Crossroad and you can use them straight away:\n\n```js\n// setUrl() is quite flexible:\nconst [url, setUrl] = useUrl();\n\nsetUrl(\"/#firsttime\"); // [Shorthand] Redirect to home with a hashtag\nsetUrl({ path: \"/\", hash: \"firsttime\" }); // Same as above\nsetUrl({ ...url, path: \"/\" }); // Keep everything the same except the path\nsetUrl({ ...url, query: { search: myQuery } }); // Set a full search query\nsetUrl({ ...url, query: { ...url.query, safe: 0 } }); // Modify only one query param\n```\n\n```js\n// In /?search=myname\u0026filter=new\n\n// Manipulate the whole query object\nconst [query, setQuery] = useQuery();\nsetQuery({ ...query, search: \"myname2\" });\n\n// Manipulate _only_ the query parameter \"search\"\nconst [search, setSearch] = useQuery(\"search\");\nsetSearch(\"myname2\");\n```\n\nSee a video and a demo of how useful and easy it is to use `useQuery()`:\n\n[**Codesandbox demo**](https://codesandbox.io/s/festive-murdock-1ctv6?file=/src/SearchForm.js)\n\nhttps://user-images.githubusercontent.com/2801252/132189338-e09aa220-b773-43ed-803b-fa6c7449bf44.mp4\n\n### Plain Links\n\nTo add a link in your application, you use the native `\u003ca\u003e` element instead of having to import a different component. What's more, this makes links a lot more consistent than in React Router. Some examples:\n\n```js\n// Crossroad\n\n// Normal link\n\u003ca href=\"/\"\u003eHello\u003c/a\u003e\n\n// Open in same page with refresh\n\u003ca href=\"https://example.com/\"\u003eHello\u003c/a\u003e // http(s): links open with refresh\n\u003ca href=\"/\" target=\"_self\"\u003eHello\u003c/a\u003e  // self to open with refresh\n\n// Open in new page\n\u003ca href=\"/myfile.pdf\" target=\"_blank\"\u003eHello\u003c/a\u003e  // Traditional target blank\n\u003ca href=\"https://example.com/\" target=\"_blank\"\u003eHello\u003c/a\u003e\n```\n\nThe same in React Router are like this, note the inconsistencies of some times using `\u003cLink\u003e` and some times using `\u003ca\u003e`\n\n```js\n// React Router\n\n// Normal link\n\u003cLink to=\"/\"\u003eHello\u003c/Link\u003e\n\n// Open in same page with refresh\n\u003ca href=\"https://example.com/\"\u003eHello\u003c/Link\u003e\n\u003ca href=\"/\"\u003eHello\u003c/Link\u003e\n\n// Open in new page\n\u003ca href=\"https://example.com/\" target=\"_blank\"\u003eHello\u003c/Link\u003e\n\u003cLink to=\"https://example.com/\"\u003eHello\u003c/Link\u003e  // Broken\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffranciscop%2Fcrossroad","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffranciscop%2Fcrossroad","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffranciscop%2Fcrossroad/lists"}