CoolFace
Apppublic

AK-21/Graphite-Industrial-Intelligence

sourceHugging Faceupdated 3mo agoView on Hugging Face
0likes
README.md1088 linesDownload Raw Back to wouter
1<div align="center">2  <img src="assets/logo.svg" width="80" alt="Wouter — a super-tiny React router (logo by Katya Simacheva)" />3</div>4 5<br />6 7<div align="center">8  <a href="https://npmjs.org/package/wouter"><img alt="npm" src="https://img.shields.io/npm/v/wouter.svg?color=black&labelColor=888" /></a>9  <a href="https://travis-ci.org/molefrog/wouter"><img alt="CI" src="https://img.shields.io/github/actions/workflow/status/molefrog/wouter/size.yml?color=black&labelColor=888&label=2.5KB+limit" /></a>10  <a href="https://coveralls.io/github/molefrog/wouter?branch=v3"><img alt="Coverage" src="https://img.shields.io/coveralls/github/molefrog/wouter/v3.svg?color=black&labelColor=888" /></a>11  <a href="https://www.npmjs.com/package/wouter"><img alt="Coverage" src="https://img.shields.io/npm/dm/wouter.svg?color=black&labelColor=888" /></a>12  <a href="https://pr.new/molefrog/wouter"><img alt="Edit in StackBlitz IDE" src="https://img.shields.io/badge/StackBlitz-New%20PR-black?labelColor=888" /></a>13</div>14 15<div align="center">16  <b>wouter</b> is a tiny router for modern React and Preact apps that relies on Hooks. <br />17  A router you wanted so bad in your project!<br>18</div>19 20## Features21 22<img src="assets/wouter.svg" align="right" width="250" alt="by Katya Simacheva" />23 24- Minimum dependencies, only **2.1 KB** gzipped vs 18.7KB25  [React Router](https://github.com/ReactTraining/react-router).26- Supports both **React** and **[Preact](https://preactjs.com/)**! Read27  _["Preact support" section](#preact-support)_ for more details.28- No top-level `<Router />` component, it is **fully optional**.29- Mimics [React Router](https://github.com/ReactTraining/react-router)'s best practices by providing30  familiar **[`Route`](#route-pathpattern-)**, **[`Link`](#link-hrefpath-)**,31  **[`Switch`](#switch-)** and **[`Redirect`](#redirect-topath-)** components.32- Has hook-based API for more granular control over routing (like animations):33  **[`useLocation`](#uselocation-working-with-the-history)**,34  **[`useRoute`](#useroute-route-matching-and-parameters)** and35  **[`useRouter`](#userouter-accessing-the-router-object)**.36 37## developers :sparkling_heart: wouter38 39> ... I love Wouter. It’s tiny, fully embraces hooks, and has an intuitive and barebones API. I can40> accomplish everything I could with react-router with Wouter, and it just feels **more minimalist41> while not being inconvenient.**42>43> [**Matt Miller**, _An exhaustive React ecosystem for 2020_](https://medium.com/@mmiller42/an-exhaustive-react-guide-for-2020-7859f0bddc56)44 45Wouter provides a simple API that many developers and library authors appreciate. Some notable46projects that use wouter: **[Ultra](https://ultrajs.dev/)**,47**[React-three-fiber](https://github.com/react-spring/react-three-fiber)**,48**[Sunmao UI](https://sunmao-ui.com/)**, **[Million](https://million.dev/)** and many more.49 50## Table of Contents51 52- [Getting Started](#getting-started)53  - [Browser Support](#browser-support)54- [Wouter API](#wouter-api)55  - [The list of methods available](#the-list-of-methods-available)56- [Hooks API](#hooks-api)57  - [`useRoute`: route matching and parameters](#useroute-route-matching-and-parameters)58  - [`useLocation`: working with the history](#uselocation-working-with-the-history)59    - [Additional navigation parameters](#additional-navigation-parameters)60    - [Customizing the location hook](#customizing-the-location-hook)61  - [`useParams`: extracting matched parameters](#useparams-extracting-matched-parameters)62  - [`useSearch`: query strings](#usesearch-query-strings)63  - [`useSearchParams`: search parameters](#usesearchparams-search-parameters)64  - [`useRouter`: accessing the router object](#userouter-accessing-the-router-object)65- [Component API](#component-api)66 67  - [`<Route path={pattern} />`](#route-pathpattern-)68    - [Route nesting](#route-nesting)69  - [`<Link href={path} />`](#link-hrefpath-)70  - [`<Switch />`](#switch-)71  - [`<Redirect to={path} />`](#redirect-topath-)72  - [`<Router hook={hook} parser={fn} base={basepath} />`](#router-hookhook-parserfn-basebasepath-hrefsfn-)73 74- [FAQ and Code Recipes](#faq-and-code-recipes)75  - [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)76  - [How do I make a default route?](#how-do-i-make-a-default-route)77  - [How do I make a link active for the current route?](#how-do-i-make-a-link-active-for-the-current-route)78  - [Are strict routes supported?](#are-strict-routes-supported)79  - [Are relative routes and links supported?](#are-relative-routes-and-links-supported)80  - [Can I initiate navigation from outside a component?](#can-i-initiate-navigation-from-outside-a-component)81  - [Can I use _wouter_ in my TypeScript project?](#can-i-use-wouter-in-my-typescript-project)82  - [How can add animated route transitions?](#how-can-add-animated-route-transitions)83  - [How do I add view transitions to my app?](#how-do-i-add-view-transitions-to-my-app)84  - [Preact support?](#preact-support)85  - [Server-side Rendering support (SSR)?](#server-side-rendering-support-ssr)86  - [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)87  - [1KB is too much, I can't afford it!](#1kb-is-too-much-i-cant-afford-it)88- [Acknowledgements](#acknowledgements)89 90## Getting Started91 92First, add wouter to your project.93 94```bash95npm i wouter96```97 98Or, if you're using Preact the use the following command [`npm i wouter-preact`](#preact-support).99 100Check 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.101 102```js103import { Link, Route, Switch } from "wouter";104 105const App = () => (106  <>107    <Link href="/users/1">Profile</Link>108 109    <Route path="/about">About Us</Route>110 111    {/* 112      Routes below are matched exclusively -113      the first matched route gets rendered114    */}115    <Switch>116      <Route path="/inbox" component={InboxPage} />117 118      <Route path="/users/:name">119        {(params) => <>Hello, {params.name}!</>}120      </Route>121 122      {/* Default route in a switch */}123      <Route>404: No such page!</Route>124    </Switch>125  </>126);127```128 129### Browser Support130 131This 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.132 133## Wouter API134 135Wouter comes with three kinds of APIs: low-level **standalone location hooks**, hooks for **routing and pattern matching** and more traditional **component-based136API** similar to React Router's one.137 138You are free to choose whatever works for you: use location hooks when you want to keep your app as small as139possible and don't need pattern matching; use routing hooks when you want to build custom routing components; or if you're building a traditional app140with pages and navigation — components might come in handy.141 142Check out also [FAQ and Code Recipes](#faq-and-code-recipes) for more advanced things like active143links, default routes, server-side rendering etc.144 145### The list of methods available146 147**Location Hooks**148 149These can be used separately from the main module and have an interface similar to `useState`. These hooks are standalone and don't include built-in support for nesting, base path, or route matching. However, when passed to `<Router>`, they work seamlessly with all Router features including nesting and base paths.150 151- **[`import { useBrowserLocation } from "wouter/use-browser-location"`](https://github.com/molefrog/wouter/blob/v3/packages/wouter/src/use-browser-location.js)** —152  allows to manipulate current location in the browser's address bar, a tiny wrapper around the History API.153- **[`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 `#`.154- **[`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).155 156**Routing Hooks**157 158Import from `wouter` module.159 160- **[`useRoute`](#useroute-the-power-of-hooks)** — shows whether or not current page matches the161  pattern provided.162- **[`useLocation`](#uselocation-working-with-the-history)** — allows to manipulate current163  router's location, by default subscribes to browser location. **Note:** this isn't the same as `useBrowserLocation`, read below.164- **[`useParams`](#useparams-extracting-matched-parameters)** — returns an object with parameters matched from the closest route.165- **[`useSearch`](#usesearch-query-strings)** — returns a search string – everything that goes after the `?`.166- **[`useRouter`](#userouter-accessing-the-router-object)** — returns a global router object that167  holds the configuration. Only use it if you want to customize the routing.168 169**Components**170 171Import from `wouter` module.172 173- **[`<Route />`](#route-pathpattern-)** — conditionally renders a component based on a pattern.174- **[`<Link />`](#link-hrefpath-)** — wraps `<a>`, allows to perform a navigation.175- **[`<Switch />`](#switch-)** — exclusive routing, only renders the first matched route.176- **[`<Redirect />`](#redirect-topath-)** — when rendered, performs an immediate navigation.177- **[`<Router />`](#router-hookhook-matchermatchfn-basebasepath-)** — an optional top-level178  component for advanced routing configuration.179 180## Hooks API181 182### `useRoute`: route matching and parameters183 184Checks 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.185 186You can use `useRoute` to perform manual routing or implement custom logic, such as route transitions, etc.187 188```js189import { useRoute } from "wouter";190 191const Users = () => {192  // `match` is a boolean193  const [match, params] = useRoute("/users/:name");194 195  if (match) {196    return <>Hello, {params.name}!</>;197  } else {198    return null;199  }200};201```202 203A quick cheatsheet of what types of segments are supported:204 205```js206useRoute("/app/:page");207useRoute("/app/:page/:section");208 209// optional parameter, matches "/en/home" and "/home"210useRoute("/:locale?/home");211 212// suffixes213useRoute("/movies/:title.(mp4|mov)");214 215// wildcards, matches "/app", "/app-1", "/app/home"216useRoute("/app*");217 218// optional wildcards, matches "/orders", "/orders/"219// and "/orders/completed/list"220useRoute("/orders/*?");221 222// regex for matching complex patterns,223// matches "/hello:123"224useRoute(/^[/]([a-z]+):([0-9]+)[/]?$/);225// and with named capture groups226useRoute(/^[/](?<word>[a-z]+):(?<num>[0-9]+)[/]?$/);227```228 229The 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 `"*"`:230 231```js232// wildcards, matches "/app", "/app-1", "/app/home"233const [match, params] = useRoute("/app*");234 235if (match) {236  // "/home" for "/app/home"237  const page = params["*"];238}239```240 241### `useLocation`: working with the history242 243To 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.244 245By 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.246 247```js248import { useLocation } from "wouter";249 250const CurrentLocation = () => {251  const [location, navigate] = useLocation();252 253  return (254    <div>255      {`The current page is: ${location}`}256      <a onClick={() => navigate("/somewhere")}>Click to update</a>257    </div>258  );259};260```261 262All the components internally call the `useLocation` hook.263 264#### Additional navigation parameters265 266The setter method of `useLocation` can also accept an optional object with parameters to control how267the navigation update will happen.268 269When browser location is used (default), `useLocation` hook accepts `replace` flag to tell the hook to modify the current270history entry instead of adding a new one. It is the same as calling `replaceState`.271 272```jsx273const [location, navigate] = useLocation();274 275navigate("/jobs"); // `pushState` is used276navigate("/home", { replace: true }); // `replaceState` is used277```278 279Additionally, you can provide a `state` option to update `history.state` while navigating:280 281```jsx282navigate("/home", { state: { modal: "promo" } });283 284history.state; // { modal: "promo" }285```286 287#### Customizing the location hook288 289By default, **wouter** uses `useLocation` hook that reacts to `pushState` and `replaceState`290navigation via `useBrowserLocation`.291 292To customize this, wrap your app in a `Router` component:293 294```js295import { Router, Route } from "wouter";296import { useHashLocation } from "wouter/use-hash-location";297 298const App = () => (299  <Router hook={useHashLocation}>300    <Route path="/about" component={About} />301    ...302  </Router>303);304```305 306Because 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!307 308### `useParams`: extracting matched parameters309 310This 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.311 312This 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.313 314```js315import { Route, useParams } from "wouter";316 317const User = () => {318  const params = useParams();319 320  params.id; // "1"321 322  // alternatively, use the index to access the prop323  params[0]; // "1"324};325 326<Route path="/user/:id" component={User}> />327```328 329It 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.330 331```js332import { Route, useParams } from "wouter";333 334const User = () => {335  const params = useParams();336 337  params.id; // "1"338  params[0]; // "1"339};340 341<Route path={/^[/]user[/](?<id>[0-9]+)[/]?$/} component={User}> />342```343 344### `useSearch`: query strings345 346Use 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.347 348```jsx349import { useSearch } from "wouter";350 351// returns "tab=settings&id=1"352const searchString = useSearch();353```354 355For the SSR, use `ssrSearch` prop passed to the router.356 357```jsx358<Router ssrSearch={request.search}>{/* SSR! */}</Router>359```360 361Refer to [Server-Side Rendering](#server-side-rendering-support-ssr) for more info on rendering and hydration.362 363### `useSearchParams`: search parameters364 365Returns a `URLSearchParams` object and a setter function to update search parameters. The setter accepts either a value (object, URLSearchParams, string[][], etc.) or a **callback function** that receives the current params and must return the new params.366 367```jsx368import { useSearchParams } from 'wouter';369 370const [searchParams, setSearchParams] = useSearchParams();371 372// extract a specific search parameter373const id = searchParams.get('id');374 375// modify a specific search parameter376setSearchParams((prev) => {377  prev.set('tab', 'settings');378  return prev;379});380 381// override all search parameters382setSearchParams({383  id: 1234,384  tab: 'settings',385});386 387// by default, setSearchParams() will push a new history entry388// to avoid this, set `replace` option to `true`389setSearchParams(390  (prev) => {391    prev.set('order', 'desc');392    return prev;393  },394  {395    replace: true,396  },397);398 399// you can also pass a history state in options400setSearchParams(401  (prev) => {402    prev.set('foo', 'bar');403    return prev;404  },405  {406    state: 'hello',407  },408);409```410 411### `useRouter`: accessing the router object412 413If you're building advanced integration, for example custom location hook, you might want to get414access to the global router object. Router is a simple object that holds routing options that you configure in the `Router` component.415 416```js417import { useRouter } from "wouter";418 419const Custom = () => {420  const router = useRouter();421 422  router.hook; // `useBrowserLocation` by default423  router.base; // "/app"424};425 426const App = () => (427  <Router base="/app">428    <Custom />429  </Router>430);431```432 433## Component API434 435### `<Route path={pattern} />`436 437`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).438 439The library provides multiple ways to declare a route's body:440 441```js442import { Route } from "wouter";443 444// simple form445<Route path="/home"><Home /></Route>446 447// render-prop style448<Route path="/users/:id">449  {params => <UserPage id={params.id} />}450</Route>451 452// the `params` prop will be passed down to <Orders />453<Route path="/orders/:status" component={Orders} />454```455 456A route with no path is considered to always match, and it is the same as `<Route path="*" />`. When developing your app, use this trick to peek at the route's content without navigation.457 458```diff459-<Route path="/some/page">460+<Route>461  {/* Strip out the `path` to make this visible */}462</Route>463```464 465#### Route Nesting466 467Nesting 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.468 469Let's take a look at this example:470 471```js472<Route path="/app" nest>473  <Route path="/users/:id" nest>474    <Route path="/orders" />475  </Route>476</Route>477```478 4791. This first route will be active for all paths that start with `/app`, this is equivalent to having a base path in your app.480 4812. The second one uses dynamic pattern to match paths like `/app/user/1`, `/app/user/1/anything` and so on.482 4833. 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.484 485If 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:486 487```js488<Route path="/payments" nest>489  <Route path="/all">490    <Link to="~/home">Back to Home</Link>491  </Route>492</Route>493```494 495**Note:** The `nest` prop does not alter the regex passed into regex paths.496Instead, the `nest` prop will only determine if nested routes will match against the rest of path or the same path.497To make a strict path regex, use a regex pattern like `/^[/](your pattern)[/]?$/` (this matches an optional end slash and the end of the string).498To 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).499 500### `<Link href={path} />`501 502Link component renders an `<a />` element that, when clicked, performs a navigation.503 504```js505import { Link } from "wouter"506 507<Link href="/">Home</Link>508 509// `to` is an alias for `href`510<Link to="/">Home</Link>511 512// all standard `a` props are proxied513<Link href="/" className="link" aria-label="Go to homepage">Home</Link>514 515// all location hook options are supported516<Link href="/" replace state={{ animate: true }} />517```518 519Link will always wrap its children in an `<a />` tag, unless `asChild` prop is provided. Use this when you need to have a custom component that renders an `<a />` under the hood.520 521```jsx522// use this instead523<Link to="/" asChild>524  <UIKitLink />525</Link>526 527// Remember, `UIKitLink` must implement an `onClick` handler528// in order for navigation to work!529```530 531When 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)532 533```jsx534<Link className={(active) => (active ? "active" : "")}>Nav</Link>535```536 537Read more about [active links here](#how-do-i-make-a-link-active-for-the-current-route).538 539### `<Switch />`540 541There are cases when you want to have an exclusive routing: to make sure that only one route is542rendered at the time, even if the routes have patterns that overlap. That's what `Switch` does: it543only renders **the first matching route**.544 545```js546import { Route, Switch } from "wouter";547 548<Switch>549  <Route path="/orders/all" component={AllOrders} />550  <Route path="/orders/:status" component={Orders} />551 552  {/* 553     in wouter, any Route with empty path is considered always active. 554     This can be used to achieve "default" route behaviour within Switch. 555     Note: the order matters! See examples below.556  */}557  <Route>This is rendered when nothing above has matched</Route>558</Switch>;559```560 561When 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.562 563### `<Redirect to={path} />`564 565When mounted performs a redirect to a `path` provided. Uses `useLocation` hook internally to trigger566the navigation inside of a `useEffect` block.567 568`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.569 570```jsx571<Redirect to="/" />572 573// arbitrary state object574<Redirect to="/" state={{ modal: true }} />575 576// use `replaceState`577<Redirect to="/" replace />578```579 580If you need more advanced logic for navigation, for example, to trigger the redirect inside of an581event handler, consider using582[`useLocation` hook instead](#uselocation-working-with-the-history):583 584```js585import { useLocation } from "wouter";586 587const [location, setLocation] = useLocation();588 589fetchOrders().then((orders) => {590  setOrders(orders);591  setLocation("/app/orders");592});593```594 595### `<Router hook={hook} parser={fn} base={basepath} hrefs={fn} />`596 597Unlike _React Router_, routes in wouter **don't have to be wrapped in a top-level component**. An598internal router object will be constructed on demand, so you can start writing your app without599polluting it with a cascade of top-level providers. There are cases however, when the routing600behaviour needs to be customized.601 602These cases include hash-based routing, basepath support, custom matcher function etc.603 604```jsx605import { useHashLocation } from "wouter/use-hash-location";606 607<Router hook={useHashLocation} base="/app">608  {/* Your app goes here */}609</Router>;610```611 612A router is a simple object that holds the routing configuration options. You can always obtain this613object using a [`useRouter` hook](#userouter-accessing-the-router-object). The list of currently614available options:615 616- **`hook: () => [location: string, setLocation: fn]`** — is a React Hook function that subscribes617  to location changes. It returns a pair of current `location` string e.g. `/app/users` and a618  `setLocation` function for navigation. You can use this hook from any component of your app by619  calling [`useLocation()` hook](#uselocation-working-with-the-history). See [Customizing the location hook](#customizing-the-location-hook).620 621- **`searchHook: () => [search: string, setSearch: fn]`** — similar to `hook`, but for obtaining the [current search string](#usesearch-query-strings).622 623- **`base: string`** — an optional setting that allows to specify a base path, such as `/app`. All624  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).625 626- **`parser: (path: string, loose?: boolean) => { pattern, keys }`** — a pattern parsing627  function. Produces a RegExp for matching the current location against the user-defined patterns like628  `/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.629 630- **`ssrPath: string`** and **`ssrSearch: string`** use these when [rendering your app on the server](#server-side-rendering-support-ssr).631 632- `hrefs: (href: boolean) => string` — a function for transforming `href` attribute of an `<a />` 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.633 634- **`aroundNav: (navigate, to, options) => void`** — a handler that wraps all navigation calls. Use this to intercept navigation and perform custom logic before and after the navigation occurs. You can modify navigation parameters, add side effects, or prevent navigation entirely. This is particularly useful for implementing [view transitions](#how-do-i-add-view-transitions-to-my-app). By default, it simply calls `navigate(to, options)`.635 636  ```js637  const aroundNav = (navigate, to, options) => {638    // do something before navigation639    navigate(to, options); // perform navigation640    // do something after navigation641  };642  ```643 644## FAQ and Code Recipes645 646### I deploy my app to the subfolder. Can I specify a base path?647 648You can! Wrap your app with `<Router base="/app" />` component and that should do the trick:649 650```js651import { Router, Route, Link } from "wouter";652 653const App = () => (654  <Router base="/app">655    {/* the link's href attribute will be "/app/users" */}656    <Link href="/users">Users</Link>657 658    <Route path="/users">The current path is /app/users!</Route>659  </Router>660);661```662 663Calling `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.664 665When you have multiple nested routers, base paths are inherited and stack up.666 667```js668<Router base="/app">669  <Router base="/cms">670    <Route path="/users">Path is /app/cms/users!</Route>671  </Router>672</Router>673```674 675### How do I make a default route?676 677One of the common patterns in application routing is having a default route that will be shown as a678fallback, in case no other route matches (for example, if you need to render 404 message). In679**wouter** this can easily be done as a combination of `<Switch />` component and a default route:680 681```js682import { Switch, Route } from "wouter";683 684<Switch>685  <Route path="/about">...</Route>686  <Route>404, Not Found!</Route>687</Switch>;688```689 690_Note:_ the order of switch children matters, default route should always come last.691 692If you want to have access to the matched segment of the path you can use wildcard parameters:693 694```js695<Switch>696  <Route path="/users">...</Route>697 698  {/* will match anything that starts with /users/, e.g. /users/foo, /users/1/edit etc. */}699  <Route path="/users/*">...</Route>700 701  {/* will match everything else */}702  <Route path="*">703    {(params) => `404, Sorry the page ${params["*"]} does not exist!`}704  </Route>705</Switch>706```707 708**[▶ Demo Sandbox](https://codesandbox.io/s/wouter-v3-ts-8q532r)**709 710### How do I make a link active for the current route?711 712Instead 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`).713 714```jsx715<Link className={(active) => (active ? "active" : "")}>Nav link</Link>716```717 718If you need to control other props, such as `aria-current` or `style`, you can write your own `<Link />` wrapper719and detect if the path is active by using the `useRoute` hook.720 721```js722const [isActive] = useRoute(props.href);723 724return (725  <Link {...props} asChild>726    <a style={isActive ? { color: "red" } : {}}>{props.children}</a>727  </Link>728);729```730 731**[▶ Demo Sandbox](https://codesandbox.io/s/wouter-v3-ts-8q532r?file=/src/ActiveLink.tsx)**732 733### Are strict routes supported?734 735If 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`.736 737Let'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.738 739```js740import { pathToRegexp } from "path-to-regexp";741 742/**743 * Custom parser based on `pathToRegexp` with strict route option744 */745const strictParser = (path, loose) => {746  const keys = [];747  const pattern = pathToRegexp(path, keys, { strict: true, end: !loose });748 749  return {750    pattern,751    // `pathToRegexp` returns some metadata about the keys,752    // we want to strip it to just an array of keys753    keys: keys.map((k) => k.name),754  };755};756 757const App = () => (758  <Router parser={strictParser}>759    <Route path="/foo">...</Route>760    <Route path="/foo/">...</Route>761  </Router>762);763```764 765**[▶ Demo Sandbox](https://codesandbox.io/p/sandbox/wouter-v3-strict-routes-w3xdtz)**766 767### Are relative routes and links supported?768 769Yes! Any route with `nest` prop present creates a nesting context. Keep in mind, that the location inside a nested route will be scoped.770 771```js772const App = () => (773  <Router base="/app">774    <Route path="/dashboard" nest>775      {/* the href is "/app/dashboard/users" */}776      <Link to="/users" />777 778      <Route path="/users">779        {/* Here `useLocation()` returns "/users"! */}780      </Route>781    </Route>782  </Router>783);784```785 786**[▶ Demo Sandbox](https://codesandbox.io/p/sandbox/wouter-v3-nested-routes-l8p23s)**787 788### Can I initiate navigation from outside a component?789 790Yes, the `navigate` function is exposed from the `"wouter/use-browser-location"` module:791 792```js793import { navigate } from "wouter/use-browser-location";794 795navigate("/", { replace: true });796```797 798It's the same function that is used internally.799 800### Can I use _wouter_ in my TypeScript project?801 802Yes! Although the project isn't written in TypeScript, the type definition files are bundled with803the package.804 805### How can add animated route transitions?806 807Let's take look at how wouter routes can be animated with [`framer-motion`](framer.com/motion).808Animating 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.809 810Unfortunately, `AnimatePresence` only animates its **direct children**, so this won't work:811 812```jsx813import { motion, AnimatePresence } from "framer-motion";814 815export const MyComponent = () => (816  <AnimatePresence>817    {/* This will not work! `motion.div` is not a direct child */}818    <Route path="/">819      <motion.div820        initial={{ opacity: 0 }}821        animate={{ opacity: 1 }}822        exit={{ opacity: 0 }}823      />824    </Route>825  </AnimatePresence>826);827```828 829The workaround is to match this route manually with `useRoute`:830 831```jsx832export const MyComponent = ({ isVisible }) => {833  const [isMatch] = useRoute("/");834 835  return (836    <AnimatePresence>837      {isMatch && (838        <motion.div839          initial={{ opacity: 0 }}840          animate={{ opacity: 1 }}841          exit={{ opacity: 0 }}842        />843      )}844    </AnimatePresence>845  );846};847```848 849More 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.850 851### How do I use wouter with View Transitions API?852 853Wouter works seamlessly with the [View Transitions API](https://developer.mozilla.org/en-US/docs/Web/API/View_Transitions_API), but you'll need to manually activate it. This is because view transitions require synchronous DOM rendering and must be wrapped in `flushSync` from `react-dom`. Following wouter's philosophy of staying lightweight and avoiding unnecessary dependencies, view transitions aren't built-in. However, there's a simple escape hatch to enable them: the `aroundNav` prop.854 855```jsx856import { flushSync } from "react-dom";857import { Router, type AroundNavHandler } from "wouter";858 859const aroundNav: AroundNavHandler = (navigate, to, options) => {860  // Check if View Transitions API is supported861  if (!document.startViewTransition) {862    navigate(to, options);863    return;864  }865 866  document.startViewTransition(() => {867    flushSync(() => {868      navigate(to, options);869    });870  });871};872 873const App = () => (874  <Router aroundNav={aroundNav}>875    {/* Your routes here */}876  </Router>877);878```879 880You can also enable transitions selectively using the `transition` prop, which will be available in the `options` parameter:881 882```jsx883// Enable transition for a specific link884<Link to="/about" transition>About</Link>885 886// Or programmatically887const [location, navigate] = useLocation();888navigate("/about", { transition: true });889 890// Then check for it in your handler891const aroundNav: AroundNavHandler = (navigate, to, options) => {892  if (!document.startViewTransition) {893    navigate(to, options);894    return;895  }896 897  if (options?.transition) {898    document.startViewTransition(() => {899      flushSync(() => {900        navigate(to, options);901      });902    });903  } else {904    navigate(to, options);905  }906};907```908 909### Preact support?910 911Preact exports are available through a separate package named `wouter-preact` (or within the912`wouter/preact` namespace, however this method isn't recommended as it requires React as a peer913dependency):914 915```diff916- import { useRoute, Route, Switch } from "wouter";917+ import { useRoute, Route, Switch } from "wouter-preact";918```919 920You might need to ensure you have the latest version of921[Preact X](https://github.com/preactjs/preact/releases/tag/10.0.0-alpha.0) with support for hooks.922 923**[▶ Demo Sandbox](https://codesandbox.io/s/wouter-preact-0lr3n)**924 925### Server-side Rendering support (SSR)?926 927In order to render your app on the server, you'll need to wrap your app with top-level Router and928specify `ssrPath` prop (usually, derived from current request). Optionally, `Router` accepts `ssrSearch` parameter if need to have access to a search string on a server.929 930```js931import { renderToString } from "react-dom/server";932import { Router } from "wouter";933 934const handleRequest = (req, res) => {935  // top-level Router is mandatory in SSR mode936  // pass an optional context object to handle redirects on the server937  const ssrContext = {};938  const prerendered = renderToString(939    <Router ssrPath={req.path} ssrSearch={req.search} ssrContext={ssrContext}>940      <App />941    </Router>942  );943 944  if (ssrContext.redirectTo) {945    // encountered redirect946    res.redirect(ssrContext.redirectTo);947  } else {948    // respond with prerendered html949  }950};951```952 953Tip: wouter can pre-fill `ssrSearch`, if `ssrPath` contains the `?` character. So these are equivalent:954 955```jsx956<Router ssrPath="/goods?sort=asc" />;957 958// is the same as959<Router ssrPath="/goods" ssrSearch="sort=asc" />;960```961 962On the client, the static markup must be hydrated in order for your app to become interactive. Note963that to avoid having hydration warnings, the JSX rendered on the client must match the one used by964the server, so the `Router` component must be present.965 966```js967import { hydrateRoot } from "react-dom/client";968 969const root = hydrateRoot(970  domNode,971  // during hydration, `ssrPath` is set to `location.pathname`,972  // `ssrSearch` set to `location.search` accordingly973  // so there is no need to explicitly specify them974  <Router>975    <App />976  </Router>977);978```979 980**[▶ Demo](https://github.com/molefrog/wultra)**981 982### How do I configure the router to render a specific route in tests?983 984Testing 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`.985 986```jsx987import { render } from "@testing-library/react";988import { memoryLocation } from "wouter/memory-location";989 990it("renders a user page", () => {991  // `static` option makes it immutable992  // even if you call `navigate` somewhere in the app location won't change993  const { hook, searchHook } = memoryLocation({ path: "/user/2", static: true });994 995  const { container } = render(996    <Router hook={hook} searchHook={searchHook}>997      <Route path="/user/:id">{(params) => <>User ID: {params.id}</>}</Route>998    </Router>999  );1000 1001  expect(container.innerHTML).toBe("User ID: 2");1002});1003```1004 1005**Note:** When you pass a `hook` prop to `Router`, it will automatically inherit the `searchHook` from the hook if available (via `hook.searchHook`). This means you don't need to explicitly pass both `hook` and `searchHook` when using `memoryLocation` - just passing `hook` is enough for `useSearch()` to work correctly with query parameters.1006 1007```jsx1008it("works with query parameters", () => {1009  const { hook } = memoryLocation({ path: "/products?sort=price&order=asc" });1010 1011  const { result } = renderHook(() => useSearch(), {1012    wrapper: ({ children }) => <Router hook={hook}>{children}</Router>,1013  });1014 1015  expect(result.current).toBe("sort=price&order=asc");1016});1017```1018 1019The hook can be configured to record navigation history. Additionally, it comes with a `navigate` function for external navigation.1020 1021```jsx1022it("performs a redirect", () => {1023  const { hook, history, navigate } = memoryLocation({1024    path: "/",1025    // will store navigation history in `history`1026    record: true,1027  });1028 1029  const { container } = render(1030    <Router hook={hook}>1031      <Switch>1032        <Route path="/">Index</Route>1033        <Route path="/orders">Orders</Route>1034 1035        <Route>1036          <Redirect to="/orders" />1037        </Route>1038      </Switch>1039    </Router>1040  );1041 1042  expect(history).toStrictEqual(["/"]);1043 1044  navigate("/unknown/route");1045 1046  expect(container.innerHTML).toBe("Orders");1047  expect(history).toStrictEqual(["/", "/unknown/route", "/orders"]);1048});1049```1050 1051### 1KB is too much, I can't afford it!1052 1053We've got some great news for you! If you're a minimalist bundle-size nomad and you need a damn1054simple routing in your app, you can just use bare location hooks. For example, `useBrowserLocation` hook which is only **650 bytes gzipped**1055and manually match the current location with it:1056 1057```js1058import { useBrowserLocation } from "wouter/use-browser-location";1059 1060const UsersRoute = () => {1061  const [location] = useBrowserLocation();1062 1063  if (location !== "/users") return null;1064 1065  // render the route1066};1067```1068 1069Wouter's motto is **"Minimalist-friendly"**.1070 1071## Contributing1072 1073**Architecture principles:**1074 1075- All code is written in JavaScript for full control over size optimization1076- TypeScript definitions are maintained separately in `types/` directories1077- `wouter-preact` reuses the same source except for `react-deps.js` (Preact-specific hooks)1078- Type definitions are duplicated between packages (not ideal, but works for now)1079 1080**Development:** Tests run directly from source files (no build required). Run `npm run test` for interactive mode or `npm run test -- --run` for a single run. Use `npm run build` to build the distributable package before publishing.1081 1082## Acknowledgements1083 1084Wouter illustrations and logos were made by [Katya Simacheva](https://simachevakatya.com/) and1085[Katya Vakulenko](https://katyavakulenko.com/). Thank you to **[@jeetiss](https://github.com/jeetiss)**1086and all the amazing [contributors](https://github.com/molefrog/wouter/graphs/contributors) for1087helping with the development.1088