{"id":13400434,"url":"https://github.com/molefrog/wouter","last_synced_at":"2025-05-13T15:03:05.667Z","repository":{"id":37431861,"uuid":"180418592","full_name":"molefrog/wouter","owner":"molefrog","description":"🥢 A minimalist-friendly ~2.1KB routing for React and Preact","archived":false,"fork":false,"pushed_at":"2025-04-30T19:36:44.000Z","size":877,"stargazers_count":7224,"open_issues_count":30,"forks_count":168,"subscribers_count":37,"default_branch":"v3","last_synced_at":"2025-05-05T22:36:16.964Z","etag":null,"topics":["hacktoberfest","microlibrary","preact","react","react-hooks","react-router","router","typescript","zero-dependency"],"latest_commit_sha":null,"homepage":"https://npm.im/wouter","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"unlicense","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/molefrog.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,"zenodo":null},"funding":{"github":["molefrog"]}},"created_at":"2019-04-09T17:36:41.000Z","updated_at":"2025-05-05T20:34:50.000Z","dependencies_parsed_at":"2022-07-14T09:22:27.629Z","dependency_job_id":"5c3dbb1a-26c7-4d82-a3c5-a0a52e8df1ea","html_url":"https://github.com/molefrog/wouter","commit_stats":{"total_commits":474,"total_committers":41,"mean_commits":"11.560975609756097","dds":0.310126582278481,"last_synced_commit":"7d6662d3f04920e45d69ec449da1eee22fc6d278"},"previous_names":[],"tags_count":63,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/molefrog%2Fwouter","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/molefrog%2Fwouter/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/molefrog%2Fwouter/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/molefrog%2Fwouter/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/molefrog","download_url":"https://codeload.github.com/molefrog/wouter/tar.gz/refs/heads/v3","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":253968259,"owners_count":21992253,"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":["hacktoberfest","microlibrary","preact","react","react-hooks","react-router","router","typescript","zero-dependency"],"created_at":"2024-07-30T19:00:51.986Z","updated_at":"2025-05-13T15:03:05.604Z","avatar_url":"https://github.com/molefrog.png","language":"TypeScript","readme":"\u003cdiv align=\"center\"\u003e\n  \u003cimg src=\"assets/logo.svg\" width=\"80\" alt=\"Wouter — a super-tiny React router (logo by Katya Simacheva)\" /\u003e\n\u003c/div\u003e\n\n\u003cbr /\u003e\n\n\u003cdiv align=\"center\"\u003e\n  \u003ca href=\"https://npmjs.org/package/wouter\"\u003e\u003cimg alt=\"npm\" src=\"https://img.shields.io/npm/v/wouter.svg?color=black\u0026labelColor=888\" /\u003e\u003c/a\u003e\n  \u003ca href=\"https://travis-ci.org/molefrog/wouter\"\u003e\u003cimg alt=\"CI\" src=\"https://img.shields.io/github/actions/workflow/status/molefrog/wouter/size.yml?color=black\u0026labelColor=888\u0026label=2.5KB+limit\" /\u003e\u003c/a\u003e\n  \u003ca href=\"https://codecov.io/gh/molefrog/wouter\"\u003e\u003cimg alt=\"Coverage\" src=\"https://img.shields.io/codecov/c/github/molefrog/wouter.svg?color=black\u0026labelColor=888\" /\u003e\u003c/a\u003e\n  \u003ca href=\"https://www.npmjs.com/package/wouter\"\u003e\u003cimg alt=\"Coverage\" src=\"https://img.shields.io/npm/dm/wouter.svg?color=black\u0026labelColor=888\" /\u003e\u003c/a\u003e\n  \u003ca href=\"https://pr.new/molefrog/wouter\"\u003e\u003cimg alt=\"Edit in StackBlitz IDE\" src=\"https://img.shields.io/badge/StackBlitz-New%20PR-black?labelColor=888\" /\u003e\u003c/a\u003e\n\u003c/div\u003e\n\n\u003cdiv align=\"center\"\u003e\n  \u003cb\u003ewouter\u003c/b\u003e is a tiny router for modern React and Preact apps that relies on Hooks. \u003cbr /\u003e\n  A router you wanted so bad in your project!\u003cbr\u003e\n\u003c/div\u003e\n\n## Features\n\n\u003cimg src=\"assets/wouter.svg\" align=\"right\" width=\"250\" alt=\"by Katya Simacheva\" /\u003e\n\n- Minimum dependencies, only **2.1 KB** gzipped vs 18.7KB\n  [React Router](https://github.com/ReactTraining/react-router).\n- Supports both **React** and **[Preact](https://preactjs.com/)**! Read\n  _[\"Preact support\" section](#preact-support)_ for more details.\n- No top-level `\u003cRouter /\u003e` component, it is **fully optional**.\n- Mimics [React Router](https://github.com/ReactTraining/react-router)'s best practices by providing\n  familiar **[`Route`](#route-pathpattern-)**, **[`Link`](#link-hrefpath-)**,\n  **[`Switch`](#switch-)** and **[`Redirect`](#redirect-topath-)** components.\n- Has hook-based API for more granular control over routing (like animations):\n  **[`useLocation`](#uselocation-working-with-the-history)**,\n  **[`useRoute`](#useroute-route-matching-and-parameters)** and\n  **[`useRouter`](#userouter-accessing-the-router-object)**.\n\n## developers :sparkling_heart: wouter\n\n\u003e ... I love Wouter. It’s tiny, fully embraces hooks, and has an intuitive and barebones API. I can\n\u003e accomplish everything I could with react-router with Wouter, and it just feels **more minimalist\n\u003e while not being inconvenient.**\n\u003e\n\u003e [**Matt Miller**, _An exhaustive React ecosystem for 2020_](https://medium.com/@mmiller42/an-exhaustive-react-guide-for-2020-7859f0bddc56)\n\nWouter provides a simple API that many developers and library authors appreciate. Some notable\nprojects that use wouter: **[Ultra](https://ultrajs.dev/)**,\n**[React-three-fiber](https://github.com/react-spring/react-three-fiber)**,\n**[Sunmao UI](https://sunmao-ui.com/)**, **[Million](https://million.dev/)** and many more.\n\n## Table of Contents\n\n- [Getting Started](#getting-started)\n  - [Browser Support](#browser-support)\n- [Wouter API](#wouter-api)\n  - [The list of methods available](#the-list-of-methods-available)\n- [Hooks API](#hooks-api)\n  - [`useRoute`: route matching and parameters](#useroute-route-matching-and-parameters)\n  - [`useLocation`: working with the history](#uselocation-working-with-the-history)\n    - [Additional navigation parameters](#additional-navigation-parameters)\n    - [Customizing the location hook](#customizing-the-location-hook)\n  - [`useParams`: extracting matched parameters](#useparams-extracting-matched-parameters)\n  - [`useSearch`: query strings](#usesearch-query-strings)\n  - [`useSearchParams`: search parameters](#usesearchparams-search-parameters)\n  - [`useRouter`: accessing the router object](#userouter-accessing-the-router-object)\n- [Component API](#component-api)\n\n  - [`\u003cRoute path={pattern} /\u003e`](#route-pathpattern-)\n    - [Route nesting](#route-nesting)\n  - [`\u003cLink href={path} /\u003e`](#link-hrefpath-)\n  - [`\u003cSwitch /\u003e`](#switch-)\n  - [`\u003cRedirect to={path} /\u003e`](#redirect-topath-)\n  - [`\u003cRouter hook={hook} parser={fn} base={basepath} /\u003e`](#router-hookhook-parserfn-basebasepath-hrefsfn-)\n\n- [FAQ and Code Recipes](#faq-and-code-recipes)\n  - [I deploy my app to the subfolder. Can I specify a base path?](#i-deploy-my-app-to-the-subfolder-can-i-specify-a-base-path)\n  - [How do I make a default route?](#how-do-i-make-a-default-route)\n  - [How do I make a link active for the current route?](#how-do-i-make-a-link-active-for-the-current-route)\n  - [Are strict routes supported?](#are-strict-routes-supported)\n  - [Are relative routes and links supported?](#are-relative-routes-and-links-supported)\n  - [Can I initiate navigation from outside a component?](#can-i-initiate-navigation-from-outside-a-component)\n  - [Can I use _wouter_ in my TypeScript project?](#can-i-use-wouter-in-my-typescript-project)\n  - [How can add animated route transitions?](#how-can-add-animated-route-transitions)\n  - [Preact support?](#preact-support)\n  - [Server-side Rendering support (SSR)?](#server-side-rendering-support-ssr)\n  - [How do I configure the router to render a specific route in tests?](#how-do-i-configure-the-router-to-render-a-specific-route-in-tests)\n  - [1KB is too much, I can't afford it!](#1kb-is-too-much-i-cant-afford-it)\n- [Acknowledgements](#acknowledgements)\n\n## Getting Started\n\nFirst, add wouter to your project.\n\n```bash\nnpm i wouter\n```\n\nOr, if you're using Preact the use the following command [`npm i wouter-preact`](#preact-support).\n\nCheck out this simple demo app below. It doesn't cover hooks and other features such as nested routing, but it's a good starting point for those who are migrating from React Router.\n\n```js\nimport { Link, Route, Switch } from \"wouter\";\n\nconst App = () =\u003e (\n  \u003c\u003e\n    \u003cLink href=\"/users/1\"\u003eProfile\u003c/Link\u003e\n\n    \u003cRoute path=\"/about\"\u003eAbout Us\u003c/Route\u003e\n\n    {/* \n      Routes below are matched exclusively -\n      the first matched route gets rendered\n    */}\n    \u003cSwitch\u003e\n      \u003cRoute path=\"/inbox\" component={InboxPage} /\u003e\n\n      \u003cRoute path=\"/users/:name\"\u003e\n        {(params) =\u003e \u003c\u003eHello, {params.name}!\u003c/\u003e}\n      \u003c/Route\u003e\n\n      {/* Default route in a switch */}\n      \u003cRoute\u003e404: No such page!\u003c/Route\u003e\n    \u003c/Switch\u003e\n  \u003c/\u003e\n);\n```\n\n### Browser Support\n\nThis library is designed for **ES2020+** compatibility. If you need to support older browsers, make sure that you transpile `node_modules`. Additionally, the minimum supported TypeScript version is 4.1 in order to support route parameter inference.\n\n## Wouter API\n\nWouter comes with three kinds of APIs: low-level **standalone location hooks**, hooks for **routing and pattern matching** and more traditional **component-based\nAPI** similar to React Router's one.\n\nYou are free to choose whatever works for you: use location hooks when you want to keep your app as small as\npossible and don't need pattern matching; use routing hooks when you want to build custom routing components; or if you're building a traditional app\nwith pages and navigation — components might come in handy.\n\nCheck out also [FAQ and Code Recipes](#faq-and-code-recipes) for more advanced things like active\nlinks, default routes, server-side rendering etc.\n\n### The list of methods available\n\n**Location Hooks**\n\nThese can be used separately from the main module and have an interface similar to `useState`. These hooks don't support nesting, base path, route matching.\n\n- **[`import { useBrowserLocation } from \"wouter/use-browser-location\"`](https://github.com/molefrog/wouter/blob/v3/packages/wouter/src/use-browser-location.js)** —\n  allows to manipulate current location in the browser's address bar, a tiny wrapper around the History API.\n- **[`import { useHashLocation } from \"wouter/use-hash-location\"`](https://github.com/molefrog/wouter/blob/v3/packages/wouter/src/use-hash-location.js)** — similarly, gets location from the hash part of the address, i.e. the string after a `#`.\n- **[`import { memoryLocation } from \"wouter/memory-location\"`](#uselocation-working-with-the-history)** — an in-memory location hook with history support, external navigation and immutable mode for testing. **Note** the module name because it is a high-order hook. See how memory location can be used in [testing](#how-do-i-configure-the-router-to-render-a-specific-route-in-tests).\n\n**Routing Hooks**\n\nImport from `wouter` module.\n\n- **[`useRoute`](#useroute-the-power-of-hooks)** — shows whether or not current page matches the\n  pattern provided.\n- **[`useLocation`](#uselocation-working-with-the-history)** — allows to manipulate current\n  router's location, by default subscribes to browser location. **Note:** this isn't the same as `useBrowserLocation`, read below.\n- **[`useParams`](#useparams-extracting-matched-parameters)** — returns an object with parameters matched from the closest route.\n- **[`useSearch`](#usesearch-query-strings)** — returns a search string – everything that goes after the `?`.\n- **[`useRouter`](#userouter-accessing-the-router-object)** — returns a global router object that\n  holds the configuration. Only use it if you want to customize the routing.\n\n**Components**\n\nImport from `wouter` module.\n\n- **[`\u003cRoute /\u003e`](#route-pathpattern-)** — conditionally renders a component based on a pattern.\n- **[`\u003cLink /\u003e`](#link-hrefpath-)** — wraps `\u003ca\u003e`, allows to perform a navigation.\n- **[`\u003cSwitch /\u003e`](#switch-)** — exclusive routing, only renders the first matched route.\n- **[`\u003cRedirect /\u003e`](#redirect-topath-)** — when rendered, performs an immediate navigation.\n- **[`\u003cRouter /\u003e`](#router-hookhook-matchermatchfn-basebasepath-)** — an optional top-level\n  component for advanced routing configuration.\n\n## Hooks API\n\n### `useRoute`: route matching and parameters\n\nChecks if the current location matches the pattern provided and returns an object with parameters. This is powered by a wonderful [`regexparam`](https://github.com/lukeed/regexparam) library, so all its pattern syntax is fully supported.\n\nYou can use `useRoute` to perform manual routing or implement custom logic, such as route transitions, etc.\n\n```js\nimport { useRoute } from \"wouter\";\n\nconst Users = () =\u003e {\n  // `match` is a boolean\n  const [match, params] = useRoute(\"/users/:name\");\n\n  if (match) {\n    return \u003c\u003eHello, {params.name}!\u003c/\u003e;\n  } else {\n    return null;\n  }\n};\n```\n\nA quick cheatsheet of what types of segments are supported:\n\n```js\nuseRoute(\"/app/:page\");\nuseRoute(\"/app/:page/:section\");\n\n// optional parameter, matches \"/en/home\" and \"/home\"\nuseRoute(\"/:locale?/home\");\n\n// suffixes\nuseRoute(\"/movies/:title.(mp4|mov)\");\n\n// wildcards, matches \"/app\", \"/app-1\", \"/app/home\"\nuseRoute(\"/app*\");\n\n// optional wildcards, matches \"/orders\", \"/orders/\"\n// and \"/orders/completed/list\"\nuseRoute(\"/orders/*?\");\n\n// regex for matching complex patterns,\n// matches \"/hello:123\"\nuseRoute(/^[/]([a-z]+):([0-9]+)[/]?$/);\n// and with named capture groups\nuseRoute(/^[/](?\u003cword\u003e[a-z]+):(?\u003cnum\u003e[0-9]+)[/]?$/);\n```\n\nThe second item in the pair `params` is an object with parameters or null if there was no match. For wildcard segments the parameter name is `\"*\"`:\n\n```js\n// wildcards, matches \"/app\", \"/app-1\", \"/app/home\"\nconst [match, params] = useRoute(\"/app*\");\n\nif (match) {\n  // \"/home\" for \"/app/home\"\n  const page = params[\"*\"];\n}\n```\n\n### `useLocation`: working with the history\n\nTo get the current path and navigate between pages, call the `useLocation` hook. Similarly to `useState`, it returns a value and a setter: the component will re-render when the location changes and by calling `navigate` you can update this value and perform navigation.\n\nBy default, it uses `useBrowserLocation` under the hood, though you can configure this in a top-level `Router` component (for example, if you decide at some point to switch to a hash-based routing). `useLocation` will also return scoped path when used within nested routes or with base path setting.\n\n```js\nimport { useLocation } from \"wouter\";\n\nconst CurrentLocation = () =\u003e {\n  const [location, navigate] = useLocation();\n\n  return (\n    \u003cdiv\u003e\n      {`The current page is: ${location}`}\n      \u003ca onClick={() =\u003e navigate(\"/somewhere\")}\u003eClick to update\u003c/a\u003e\n    \u003c/div\u003e\n  );\n};\n```\n\nAll the components internally call the `useLocation` hook.\n\n#### Additional navigation parameters\n\nThe setter method of `useLocation` can also accept an optional object with parameters to control how\nthe navigation update will happen.\n\nWhen browser location is used (default), `useLocation` hook accepts `replace` flag to tell the hook to modify the current\nhistory entry instead of adding a new one. It is the same as calling `replaceState`.\n\n```jsx\nconst [location, navigate] = useLocation();\n\nnavigate(\"/jobs\"); // `pushState` is used\nnavigate(\"/home\", { replace: true }); // `replaceState` is used\n```\n\nAdditionally, you can provide a `state` option to update `history.state` while navigating:\n\n```jsx\nnavigate(\"/home\", { state: { modal: \"promo\" } });\n\nhistory.state; // { modal: \"promo\" }\n```\n\n#### Customizing the location hook\n\nBy default, **wouter** uses `useLocation` hook that reacts to `pushState` and `replaceState`\nnavigation via `useBrowserLocation`.\n\nTo customize this, wrap your app in a `Router` component:\n\n```js\nimport { Router, Route } from \"wouter\";\nimport { useHashLocation } from \"wouter/use-hash-location\";\n\nconst App = () =\u003e (\n  \u003cRouter hook={useHashLocation}\u003e\n    \u003cRoute path=\"/about\" component={About} /\u003e\n    ...\n  \u003c/Router\u003e\n);\n```\n\nBecause these hooks have return values similar to `useState`, it is easy and fun to build your own location hooks: `useCrossTabLocation`, `useLocalStorage`, `useMicroFrontendLocation` and whatever routing logic you want to support in the app. Give it a try!\n\n### `useParams`: extracting matched parameters\n\nThis hook allows you to access the parameters exposed through [matching dynamic segments](#matching-dynamic-segments). Internally, we simply wrap your components in a context provider allowing you to access this data anywhere within the `Route` component.\n\nThis allows you to avoid \"prop drilling\" when dealing with deeply nested components within the route. **Note:** `useParams` will only extract parameters from the closest parent route.\n\n```js\nimport { Route, useParams } from \"wouter\";\n\nconst User = () =\u003e {\n  const params = useParams();\n\n  params.id; // \"1\"\n\n  // alternatively, use the index to access the prop\n  params[0]; // \"1\"\n};\n\n\u003cRoute path=\"/user/:id\" component={User}\u003e /\u003e\n```\n\nIt is the same for regex paths. Capture groups can be accessed by their index, or if there is a named capture group, that can be used instead.\n\n```js\nimport { Route, useParams } from \"wouter\";\n\nconst User = () =\u003e {\n  const params = useParams();\n\n  params.id; // \"1\"\n  params[0]; // \"1\"\n};\n\n\u003cRoute path={/^[/]user[/](?\u003cid\u003e[0-9]+)[/]?$/} component={User}\u003e /\u003e\n```\n\n### `useSearch`: query strings\n\nUse this hook to get the current search (query) string value. It will cause your component to re-render only when the string itself and not the full location updates. The search string returned **does not** contain a `?` character.\n\n```jsx\nimport { useSearch } from \"wouter\";\n\n// returns \"tab=settings\u0026id=1\"\nconst searchString = useSearch();\n```\n\nFor the SSR, use `ssrSearch` prop passed to the router.\n\n```jsx\n\u003cRouter ssrSearch={request.search}\u003e{/* SSR! */}\u003c/Router\u003e\n```\n\nRefer to [Server-Side Rendering](#server-side-rendering-support-ssr) for more info on rendering and hydration.\n\n### `useSearchParams`: search parameters\n\nAllow you to get and set any search parameters. The first returned value is a `URLSearchParams` object and the second returned value is a setter that accepts a `URLSearchParams` object with options.\n\n```jsx\nimport { useSearchParams } from 'wouter';\n\nconst [searchParams, setSearchParams] = useSearchParams();\n\n// extract a specific search parameter\nconst id = searchParams.get('id');\n\n// modify a specific search parameter\nsetSearchParams((prev) =\u003e {\n  prev.set('tab', 'settings');\n});\n\n// override all search parameters\nsetSearchParams({\n  id: 1234,\n  tab: 'settings',\n});\n\n// by default, setSearchParams() will push a new history entry\n// to avoid this, set `replace` option to `true`\nsetSearchParams(\n  (prev) =\u003e {\n    prev.set('order', 'desc');\n  },\n  {\n    replace: true,\n  },\n);\n\n// you can also pass a history state in options\nsetSearchParams(\n  (prev) =\u003e {\n    prev.set('foo', 'bar');\n  },\n  {\n    state: 'hello',\n  },\n);\n```\n\n### `useRouter`: accessing the router object\n\nIf you're building advanced integration, for example custom location hook, you might want to get\naccess to the global router object. Router is a simple object that holds routing options that you configure in the `Router` component.\n\n```js\nimport { useRouter } from \"wouter\";\n\nconst Custom = () =\u003e {\n  const router = useRouter();\n\n  router.hook; // `useBrowserLocation` by default\n  router.base; // \"/app\"\n};\n\nconst App = () =\u003e (\n  \u003cRouter base=\"/app\"\u003e\n    \u003cCustom /\u003e\n  \u003c/Router\u003e\n);\n```\n\n## Component API\n\n### `\u003cRoute path={pattern} /\u003e`\n\n`Route` represents a piece of the app that is rendered conditionally based on a pattern `path`. Pattern has the same syntax as the argument you pass to [`useRoute`](#useroute-route-matching-and-parameters).\n\nThe library provides multiple ways to declare a route's body:\n\n```js\nimport { Route } from \"wouter\";\n\n// simple form\n\u003cRoute path=\"/home\"\u003e\u003cHome /\u003e\u003c/Route\u003e\n\n// render-prop style\n\u003cRoute path=\"/users/:id\"\u003e\n  {params =\u003e \u003cUserPage id={params.id} /\u003e}\n\u003c/Route\u003e\n\n// the `params` prop will be passed down to \u003cOrders /\u003e\n\u003cRoute path=\"/orders/:status\" component={Orders} /\u003e\n```\n\nA route with no path is considered to always match, and it is the same as `\u003cRoute path=\"*\" /\u003e`. When developing your app, use this trick to peek at the route's content without navigation.\n\n```diff\n-\u003cRoute path=\"/some/page\"\u003e\n+\u003cRoute\u003e\n  {/* Strip out the `path` to make this visible */}\n\u003c/Route\u003e\n```\n\n#### Route Nesting\n\nNesting is a core feature of wouter and can be enabled on a route via the `nest` prop. When this prop is present, the route matches everything that starts with a given pattern and it creates a nested routing context. All child routes will receive location relative to that pattern.\n\nLet's take a look at this example:\n\n```js\n\u003cRoute path=\"/app\" nest\u003e\n  \u003cRoute path=\"/users/:id\" nest\u003e\n    \u003cRoute path=\"/orders\" /\u003e\n  \u003c/Route\u003e\n\u003c/Route\u003e\n```\n\n1. This first route will be active for all paths that start with `/app`, this is equivalent to having a base path in your app.\n\n2. The second one uses dynamic pattern to match paths like `/app/user/1`, `/app/user/1/anything` and so on.\n\n3. Finally, the inner-most route will only work for paths that look like `/app/users/1/orders`. The match is strict, since that route does not have a `nest` prop and it works as usual.\n\nIf you call `useLocation()` inside the last route, it will return `/orders` and not `/app/users/1/orders`. This creates a nice isolation and it makes it easier to make changes to parent route without worrying that the rest of the app will stop working. If you need to navigate to a top-level page however, you can use a prefix `~` to refer to an absolute path:\n\n```js\n\u003cRoute path=\"/payments\" nest\u003e\n  \u003cRoute path=\"/all\"\u003e\n    \u003cLink to=\"~/home\"\u003eBack to Home\u003c/Link\u003e\n  \u003c/Route\u003e\n\u003c/Route\u003e\n```\n\n**Note:** The `nest` prop does not alter the regex passed into regex paths.\nInstead, the `nest` prop will only determine if nested routes will match against the rest of path or the same path.\nTo make a strict path regex, use a regex pattern like `/^[/](your pattern)[/]?$/` (this matches an optional end slash and the end of the string).\nTo make a nestable regex, use a regex pattern like `/^[/](your pattern)(?=$|[/])/` (this matches either the end of the string or a slash for future segments).\n\n### `\u003cLink href={path} /\u003e`\n\nLink component renders an `\u003ca /\u003e` element that, when clicked, performs a navigation.\n\n```js\nimport { Link } from \"wouter\"\n\n\u003cLink href=\"/\"\u003eHome\u003c/Link\u003e\n\n// `to` is an alias for `href`\n\u003cLink to=\"/\"\u003eHome\u003c/Link\u003e\n\n// all standard `a` props are proxied\n\u003cLink href=\"/\" className=\"link\" aria-label=\"Go to homepage\"\u003eHome\u003c/Link\u003e\n\n// all location hook options are supported\n\u003cLink href=\"/\" replace state={{ animate: true }} /\u003e\n```\n\nLink will always wrap its children in an `\u003ca /\u003e` tag, unless `asChild` prop is provided. Use this when you need to have a custom component that renders an `\u003ca /\u003e` under the hood.\n\n```jsx\n// use this instead\n\u003cLink to=\"/\" asChild\u003e\n  \u003cUIKitLink /\u003e\n\u003c/Link\u003e\n\n// Remember, `UIKitLink` must implement an `onClick` handler\n// in order for navigation to work!\n```\n\nWhen you pass a function as a `className` prop, it will be called with a boolean value indicating whether the link is active for the current route. You can use this to style active links (e.g. for links in navigation menu)\n\n```jsx\n\u003cLink className={(active) =\u003e (active ? \"active\" : \"\")}\u003eNav\u003c/Link\u003e\n```\n\nRead more about [active links here](#how-do-i-make-a-link-active-for-the-current-route).\n\n### `\u003cSwitch /\u003e`\n\nThere are cases when you want to have an exclusive routing: to make sure that only one route is\nrendered at the time, even if the routes have patterns that overlap. That's what `Switch` does: it\nonly renders **the first matching route**.\n\n```js\nimport { Route, Switch } from \"wouter\";\n\n\u003cSwitch\u003e\n  \u003cRoute path=\"/orders/all\" component={AllOrders} /\u003e\n  \u003cRoute path=\"/orders/:status\" component={Orders} /\u003e\n\n  {/* \n     in wouter, any Route with empty path is considered always active. \n     This can be used to achieve \"default\" route behaviour within Switch. \n     Note: the order matters! See examples below.\n  */}\n  \u003cRoute\u003eThis is rendered when nothing above has matched\u003c/Route\u003e\n\u003c/Switch\u003e;\n```\n\nWhen no route in switch matches, the last empty `Route` will be used as a fallback. See [**FAQ and Code Recipes** section](#how-do-i-make-a-default-route) to read about default routes.\n\n### `\u003cRedirect to={path} /\u003e`\n\nWhen mounted performs a redirect to a `path` provided. Uses `useLocation` hook internally to trigger\nthe navigation inside of a `useEffect` block.\n\n`Redirect` can also accept props for [customizing how navigation will be performed](#additional-navigation-parameters), for example for setting history state when navigating. These options are specific to the currently used location hook.\n\n```jsx\n\u003cRedirect to=\"/\" /\u003e\n\n// arbitrary state object\n\u003cRedirect to=\"/\" state={{ modal: true }} /\u003e\n\n// use `replaceState`\n\u003cRedirect to=\"/\" replace /\u003e\n```\n\nIf you need more advanced logic for navigation, for example, to trigger the redirect inside of an\nevent handler, consider using\n[`useLocation` hook instead](#uselocation-working-with-the-history):\n\n```js\nimport { useLocation } from \"wouter\";\n\nconst [location, setLocation] = useLocation();\n\nfetchOrders().then((orders) =\u003e {\n  setOrders(orders);\n  setLocation(\"/app/orders\");\n});\n```\n\n### `\u003cRouter hook={hook} parser={fn} base={basepath} hrefs={fn} /\u003e`\n\nUnlike _React Router_, routes in wouter **don't have to be wrapped in a top-level component**. An\ninternal router object will be constructed on demand, so you can start writing your app without\npolluting it with a cascade of top-level providers. There are cases however, when the routing\nbehaviour needs to be customized.\n\nThese cases include hash-based routing, basepath support, custom matcher function etc.\n\n```jsx\nimport { useHashLocation } from \"wouter/use-hash-location\";\n\n\u003cRouter hook={useHashLocation} base=\"/app\"\u003e\n  {/* Your app goes here */}\n\u003c/Router\u003e;\n```\n\nA router is a simple object that holds the routing configuration options. You can always obtain this\nobject using a [`useRouter` hook](#userouter-accessing-the-router-object). The list of currently\navailable options:\n\n- **`hook: () =\u003e [location: string, setLocation: fn]`** — is a React Hook function that subscribes\n  to location changes. It returns a pair of current `location` string e.g. `/app/users` and a\n  `setLocation` function for navigation. You can use this hook from any component of your app by\n  calling [`useLocation()` hook](#uselocation-working-with-the-history). See [Customizing the location hook](#customizing-the-location-hook).\n\n- **`searchHook: () =\u003e [search: string, setSearch: fn]`** — similar to `hook`, but for obtaining the [current search string](#usesearch-query-strings).\n\n- **`base: string`** — an optional setting that allows to specify a base path, such as `/app`. All\n  application routes will be relative to that path. To navigate out to an absolute path, prefix your path with an `~`. [See the FAQ](#are-relative-routes-and-links-supported).\n\n- **`parser: (path: string, loose?: boolean) =\u003e { pattern, keys }`** — a pattern parsing\n  function. Produces a RegExp for matching the current location against the user-defined patterns like\n  `/app/users/:id`. Has the same interface as the [`parse`](https://github.com/lukeed/regexparam?tab=readme-ov-file#regexparamparseinput-regexp) function from `regexparam`. See [this example](#are-strict-routes-supported) that demonstrates custom parser feature.\n\n- **`ssrPath: string`** and **`ssrSearch: string`** use these when [rendering your app on the server](#server-side-rendering-support-ssr).\n\n- `hrefs: (href: boolean) =\u003e string` — a function for transforming `href` attribute of an `\u003ca /\u003e` element rendered by `Link`. It is used to support hash-based routing. By default, `href` attribute is the same as the `href` or `to` prop of a `Link`. A location hook can also define a `hook.hrefs` property, in this case the `href` will be inferred.\n\n## FAQ and Code Recipes\n\n### I deploy my app to the subfolder. Can I specify a base path?\n\nYou can! Wrap your app with `\u003cRouter base=\"/app\" /\u003e` component and that should do the trick:\n\n```js\nimport { Router, Route, Link } from \"wouter\";\n\nconst App = () =\u003e (\n  \u003cRouter base=\"/app\"\u003e\n    {/* the link's href attribute will be \"/app/users\" */}\n    \u003cLink href=\"/users\"\u003eUsers\u003c/Link\u003e\n\n    \u003cRoute path=\"/users\"\u003eThe current path is /app/users!\u003c/Route\u003e\n  \u003c/Router\u003e\n);\n```\n\nCalling `useLocation()` within a route in an app with base path will return a path scoped to the base. Meaning that when base is `\"/app\"` and pathname is `\"/app/users\"` the returned string is `\"/users\"`. Accordingly, calling `navigate` will automatically append the base to the path argument for you.\n\nWhen you have multiple nested routers, base paths are inherited and stack up.\n\n```js\n\u003cRouter base=\"/app\"\u003e\n  \u003cRouter base=\"/cms\"\u003e\n    \u003cRoute path=\"/users\"\u003ePath is /app/cms/users!\u003c/Route\u003e\n  \u003c/Router\u003e\n\u003c/Router\u003e\n```\n\n### How do I make a default route?\n\nOne of the common patterns in application routing is having a default route that will be shown as a\nfallback, in case no other route matches (for example, if you need to render 404 message). In\n**wouter** this can easily be done as a combination of `\u003cSwitch /\u003e` component and a default route:\n\n```js\nimport { Switch, Route } from \"wouter\";\n\n\u003cSwitch\u003e\n  \u003cRoute path=\"/about\"\u003e...\u003c/Route\u003e\n  \u003cRoute\u003e404, Not Found!\u003c/Route\u003e\n\u003c/Switch\u003e;\n```\n\n_Note:_ the order of switch children matters, default route should always come last.\n\nIf you want to have access to the matched segment of the path you can use wildcard parameters:\n\n```js\n\u003cSwitch\u003e\n  \u003cRoute path=\"/users\"\u003e...\u003c/Route\u003e\n\n  {/* will match anything that starts with /users/, e.g. /users/foo, /users/1/edit etc. */}\n  \u003cRoute path=\"/users/*\"\u003e...\u003c/Route\u003e\n\n  {/* will match everything else */}\n  \u003cRoute path=\"*\"\u003e\n    {(params) =\u003e `404, Sorry the page ${params[\"*\"]} does not exist!`}\n  \u003c/Route\u003e\n\u003c/Switch\u003e\n```\n\n**[▶ Demo Sandbox](https://codesandbox.io/s/wouter-v3-ts-8q532r)**\n\n### How do I make a link active for the current route?\n\nInstead of a regular `className` string, provide a function to use custom class when this link matches the current route. Note that it will always perform an exact match (i.e. `/users` will not be active for `/users/1`).\n\n```jsx\n\u003cLink className={(active) =\u003e (active ? \"active\" : \"\")}\u003eNav link\u003c/Link\u003e\n```\n\nIf you need to control other props, such as `aria-current` or `style`, you can write your own `\u003cLink /\u003e` wrapper\nand detect if the path is active by using the `useRoute` hook.\n\n```js\nconst [isActive] = useRoute(props.href);\n\nreturn (\n  \u003cLink {...props} asChild\u003e\n    \u003ca style={isActive ? { color: \"red\" } : {}}\u003e{props.children}\u003c/a\u003e\n  \u003c/Link\u003e\n);\n```\n\n**[▶ Demo Sandbox](https://codesandbox.io/s/wouter-v3-ts-8q532r?file=/src/ActiveLink.tsx)**\n\n### Are strict routes supported?\n\nIf a trailing slash is important for your app's routing, you could specify a custom parser. Parser is a method that takes a pattern string and returns a RegExp and an array of parsed key. It uses the signature of a [`parse`](https://github.com/lukeed/regexparam?tab=readme-ov-file#regexparamparseinput-regexp) function from `regexparam`.\n\nLet's write a custom parser based on a popular [`path-to-regexp`](https://github.com/pillarjs/path-to-regexp) package that does support strict routes option.\n\n```js\nimport { pathToRegexp } from \"path-to-regexp\";\n\n/**\n * Custom parser based on `pathToRegexp` with strict route option\n */\nconst strictParser = (path, loose) =\u003e {\n  const keys = [];\n  const pattern = pathToRegexp(path, keys, { strict: true, end: !loose });\n\n  return {\n    pattern,\n    // `pathToRegexp` returns some metadata about the keys,\n    // we want to strip it to just an array of keys\n    keys: keys.map((k) =\u003e k.name),\n  };\n};\n\nconst App = () =\u003e (\n  \u003cRouter parser={strictParser}\u003e\n    \u003cRoute path=\"/foo\"\u003e...\u003c/Route\u003e\n    \u003cRoute path=\"/foo/\"\u003e...\u003c/Route\u003e\n  \u003c/Router\u003e\n);\n```\n\n**[▶ Demo Sandbox](https://codesandbox.io/s/wouter-v3-strict-routes-w3xdtz)**\n\n### Are relative routes and links supported?\n\nYes! Any route with `nest` prop present creates a nesting context. Keep in mind, that the location inside a nested route will be scoped.\n\n```js\nconst App = () =\u003e (\n  \u003cRouter base=\"/app\"\u003e\n    \u003cRoute path=\"/dashboard\" nest\u003e\n      {/* the href is \"/app/dashboard/users\" */}\n      \u003cLink to=\"/users\" /\u003e\n\n      \u003cRoute path=\"/users\"\u003e\n        {/* Here `useLocation()` returns \"/users\"! */}\n      \u003c/Route\u003e\n    \u003c/Route\u003e\n  \u003c/Router\u003e\n);\n```\n\n**[▶ Demo Sandbox](https://codesandbox.io/s/wouter-v3-nested-routes-l8p23s)**\n\n### Can I initiate navigation from outside a component?\n\nYes, the `navigate` function is exposed from the `\"wouter/use-browser-location\"` module:\n\n```js\nimport { navigate } from \"wouter/use-browser-location\";\n\nnavigate(\"/\", { replace: true });\n```\n\nIt's the same function that is used internally.\n\n### Can I use _wouter_ in my TypeScript project?\n\nYes! Although the project isn't written in TypeScript, the type definition files are bundled with\nthe package.\n\n### How can add animated route transitions?\n\nLet's take look at how wouter routes can be animated with [`framer-motion`](framer.com/motion).\nAnimating enter transitions is easy, but exit transitions require a bit more work. We'll use the `AnimatePresence` component that will keep the page in the DOM until the exit animation is complete.\n\nUnfortunately, `AnimatePresence` only animates its **direct children**, so this won't work:\n\n```jsx\nimport { motion, AnimatePresence } from \"framer-motion\";\n\nexport const MyComponent = () =\u003e (\n  \u003cAnimatePresence\u003e\n    {/* This will not work! `motion.div` is not a direct child */}\n    \u003cRoute path=\"/\"\u003e\n      \u003cmotion.div\n        initial={{ opacity: 0 }}\n        animate={{ opacity: 1 }}\n        exit={{ opacity: 0 }}\n      /\u003e\n    \u003c/Route\u003e\n  \u003c/AnimatePresence\u003e\n);\n```\n\nThe workaround is to match this route manually with `useRoute`:\n\n```jsx\nexport const MyComponent = ({ isVisible }) =\u003e {\n  const [isMatch] = useRoute(\"/\");\n\n  return (\n    \u003cAnimatePresence\u003e\n      {isMatch \u0026\u0026 (\n        \u003cmotion.div\n          initial={{ opacity: 0 }}\n          animate={{ opacity: 1 }}\n          exit={{ opacity: 0 }}\n        /\u003e\n      )}\n    \u003c/AnimatePresence\u003e\n  );\n};\n```\n\nMore complex examples involve using `useRoutes` hook (similar to how React Router does it), but wouter does not ship it out-of-the-box. Please refer to [this issue](https://github.com/molefrog/wouter/issues/414#issuecomment-1954192679) for the workaround.\n\n### Preact support?\n\nPreact exports are available through a separate package named `wouter-preact` (or within the\n`wouter/preact` namespace, however this method isn't recommended as it requires React as a peer\ndependency):\n\n```diff\n- import { useRoute, Route, Switch } from \"wouter\";\n+ import { useRoute, Route, Switch } from \"wouter-preact\";\n```\n\nYou might need to ensure you have the latest version of\n[Preact X](https://github.com/preactjs/preact/releases/tag/10.0.0-alpha.0) with support for hooks.\n\n**[▶ Demo Sandbox](https://codesandbox.io/s/wouter-preact-0lr3n)**\n\n### Server-side Rendering support (SSR)?\n\nIn order to render your app on the server, you'll need to wrap your app with top-level Router and\nspecify `ssrPath` prop (usually, derived from current request). Optionally, `Router` accepts `ssrSearch` parameter if need to have access to a search string on a server.\n\n```js\nimport { renderToString } from \"react-dom/server\";\nimport { Router } from \"wouter\";\n\nconst handleRequest = (req, res) =\u003e {\n  // top-level Router is mandatory in SSR mode\n  // pass an optional context object to handle redirects on the server\n  const ssrContext = {};\n  const prerendered = renderToString(\n    \u003cRouter ssrPath={req.path} ssrSearch={req.search} ssrContext={ssrContext}\u003e\n      \u003cApp /\u003e\n    \u003c/Router\u003e\n  );\n\n  if (ssrContext.redirectTo) {\n    // encountered redirect\n    res.redirect(ssrContext.redirectTo);\n  } else {\n    // respond with prerendered html\n  }\n};\n```\n\nTip: wouter can pre-fill `ssrSearch`, if `ssrPath` contains the `?` character. So these are equivalent:\n\n```jsx\n\u003cRouter ssrPath=\"/goods?sort=asc\" /\u003e;\n\n// is the same as\n\u003cRouter ssrPath=\"/goods\" ssrSearch=\"sort=asc\" /\u003e;\n```\n\nOn the client, the static markup must be hydrated in order for your app to become interactive. Note\nthat to avoid having hydration warnings, the JSX rendered on the client must match the one used by\nthe server, so the `Router` component must be present.\n\n```js\nimport { hydrateRoot } from \"react-dom/client\";\n\nconst root = hydrateRoot(\n  domNode,\n  // during hydration, `ssrPath` is set to `location.pathname`,\n  // `ssrSearch` set to `location.search` accordingly\n  // so there is no need to explicitly specify them\n  \u003cRouter\u003e\n    \u003cApp /\u003e\n  \u003c/Router\u003e\n);\n```\n\n**[▶ Demo](https://github.com/molefrog/wultra)**\n\n### How do I configure the router to render a specific route in tests?\n\nTesting with wouter is no different from testing regular React apps. You often need a way to provide a fixture for the current location to render a specific route. This can be easily done by swapping the normal location hook with `memoryLocation`. It is an initializer function that returns a hook that you can then specify in a top-level `Router`.\n\n```jsx\nimport { render } from \"@testing-library/react\";\nimport { memoryLocation } from \"wouter/memory-location\";\n\nit(\"renders a user page\", () =\u003e {\n  // `static` option makes it immutable\n  // even if you call `navigate` somewhere in the app location won't change\n  const { hook } = memoryLocation({ path: \"/user/2\", static: true });\n\n  const { container } = render(\n    \u003cRouter hook={hook}\u003e\n      \u003cRoute path=\"/user/:id\"\u003e{(params) =\u003e \u003c\u003eUser ID: {params.id}\u003c/\u003e}\u003c/Route\u003e\n    \u003c/Router\u003e\n  );\n\n  expect(container.innerHTML).toBe(\"User ID: 2\");\n});\n```\n\nThe hook can be configured to record navigation history. Additionally, it comes with a `navigate` function for external navigation.\n\n```jsx\nit(\"performs a redirect\", () =\u003e {\n  const { hook, history, navigate } = memoryLocation({\n    path: \"/\",\n    // will store navigation history in `history`\n    record: true,\n  });\n\n  const { container } = render(\n    \u003cRouter hook={hook}\u003e\n      \u003cSwitch\u003e\n        \u003cRoute path=\"/\"\u003eIndex\u003c/Route\u003e\n        \u003cRoute path=\"/orders\"\u003eOrders\u003c/Route\u003e\n\n        \u003cRoute\u003e\n          \u003cRedirect to=\"/orders\" /\u003e\n        \u003c/Route\u003e\n      \u003c/Switch\u003e\n    \u003c/Router\u003e\n  );\n\n  expect(history).toStrictEqual([\"/\"]);\n\n  navigate(\"/unknown/route\");\n\n  expect(container.innerHTML).toBe(\"Orders\");\n  expect(history).toStrictEqual([\"/\", \"/unknown/route\", \"/orders\"]);\n});\n```\n\n### 1KB is too much, I can't afford it!\n\nWe've got some great news for you! If you're a minimalist bundle-size nomad and you need a damn\nsimple routing in your app, you can just use bare location hooks. For example, `useBrowserLocation` hook which is only **650 bytes gzipped**\nand manually match the current location with it:\n\n```js\nimport { useBrowserLocation } from \"wouter/use-browser-location\";\n\nconst UsersRoute = () =\u003e {\n  const [location] = useBrowserLocation();\n\n  if (location !== \"/users\") return null;\n\n  // render the route\n};\n```\n\nWouter's motto is **\"Minimalist-friendly\"**.\n\n## Acknowledgements\n\nWouter illustrations and logos were made by [Katya Simacheva](https://simachevakatya.com/) and\n[Katya Vakulenko](https://katyavakulenko.com/). Thank you to **[@jeetiss](https://github.com/jeetiss)**\nand all the amazing [contributors](https://github.com/molefrog/wouter/graphs/contributors) for\nhelping with the development.\n","funding_links":["https://github.com/sponsors/molefrog"],"categories":["Routing","TypeScript","Code Design","Uncategorized","六、路由管理","JavaScript","Utilities","\u003e 1k ★","**Awesome React** [![Awesome](https://cdn.rawgit.com/sindresorhus/awesome/d7305f38d29fed78fa85652e3a63e154dd8e8829/media/badge.svg)](https://github.com/sindresorhus/awesome)","Routers and URL Utils","hacktoberfest","🧰 React Toolkit","typescript","React [🔝](#readme)"],"sub_categories":["Router","Uncategorized","3.  Immutable 工具（辅助状态管理）","React","Reactive Programming","Routing"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmolefrog%2Fwouter","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmolefrog%2Fwouter","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmolefrog%2Fwouter/lists"}