πŸ₯’ A minimalist-friendly ~1.3KB routing for React and Preact. Nothing else but HOOKS.

Related tags

Router wouter
Overview
Wouter β€” a super-tiny React router (logo by Katya Simacheva)

wouter is a tiny router for modern React and Preact apps that relies on Hooks.
A router you wanted so bad in your project!

Features

by Katya Simacheva

developers πŸ’– wouter

... I love Wouter. It’s tiny, fully embraces hooks, and has an intuitive and barebones API. I can accomplish everything I could with react-router with Wouter, and it just feels more minimalist while not being inconvenient.

Matt Miller, An exhaustive React ecosystem for 2020

Wouter provides a simple API that many developers and library authors appreciate. Some notable projects that use wouter: arcade.design (React UI kit), fre, react-three-fiber, ssgl-doom-launcher, Ziro App and many more.

Table of Contents

Getting Started

Check out this demo app below in order to get started:

import { Link, Route } from "wouter";

const App = () => (
  <div>
    <Link href="/users/1">
      <a className="link">Profile</a>
    </Link>

    <Route path="/about">About Us</Route>
    <Route path="/users/:name">
      {(params) => <div>Hello, {params.name}!</div>}
    </Route>
    <Route path="/inbox" component={InboxPage} />
  </div>
);

Supporting IE11 and obsolete platforms

This library uses features like destructuring assignment and const/let declarations and doesn't ship with ES5 transpiled sources. If you aim to support browsers like IE11 and below β†’ make sure you run Babel over your node_modules

Wouter API

Wouter comes with two kinds of APIs: low-level React Hooks API and more traditional component-based API similar to React Router's one.

You are free to choose whatever works for you: use hooks when you want to keep your app as small as possible or you want to build custom routing components; or if you're building a traditional app with pages and navigation β€” components might come in handy.

Check out also FAQ and Code Recipes for more advanced things like active links, default routes etc.

The list of methods available

Hooks API:

  • useRoute β€” shows whether or not current page matches the pattern provided.
  • useLocation β€” allows to manipulate current browser location, a tiny wrapper around the History API.
  • useRouter β€” returns a global router object that holds the configuration. Only use it if you want to customize the routing.

Component API:

  • <Route /> β€” conditionally renders a component based on a pattern.
  • <Link /> β€” wraps <a>, allows to perfom a navigation.
  • <Switch /> β€” exclusive routing, only renders the first matched route.
  • <Redirect /> β€” when rendered, performs an immediate navigation.
  • <Router /> β€” an optional top-level component for advanced routing configuration.

Hooks API

useRoute: the power of HOOKS!

Hooks make creating custom interactions such as route transitions or accessing router directly easier. You can check if a particular route matches the current location by using a useRoute hook:

import { useRoute } from "wouter";
import { Transition } from "react-transition-group";

const AnimatedRoute = () => {
  // `match` is boolean
  const [match, params] = useRoute("/users/:id");

  return <Transition in={match}>Hi, this is: {params.id}</Transition>;
};

useLocation hook: working with the history

The low-level navigation in wouter is powered by the useLocation hook, which is basically a wrapper around the native browser location object. The hook rerenders when the location changes and you can also perform a navigation with it, this is very similar to how you work with values returned from the useState hook:

import { useLocation } from "wouter";

const CurrentLocation = () => {
  const [location, setLocation] = useLocation();

  return (
    <div>
      {`The current page is: ${location}`}
      <a onClick={() => setLocation("/somewhere")}>Click to update</a>
    </div>
  );
};

All the components including the useRoute rely on useLocation hook, so normally you only need the hook to perform the navigation using a second value setLocation. You can check out the source code of the Redirect component as a reference.

Customizing the location hook

By default, wouter uses useLocation hook that reacts to pushState and replaceState navigation and observes the current pathname including the leading slash e.g. /app/users.

If you do need a custom history observer, for example, for hash-based routing, you can implement your own hook and customize it in a <Router /> component.

As an exercise, let's implement a simple location hook that listens to hash changes:

import { useState, useEffect } from "react";
import { Router, Route } from "wouter";

// returns the current hash location in a normalized form
// (excluding the leading '#' symbol)
const currentLocation = () => {
  return window.location.hash.replace(/^#/, "") || "/";
};

const navigate = (to) => (window.location.hash = to);

const useHashLocation = () => {
  const [loc, setLoc] = useState(currentLocation());

  useEffect(() => {
    // this function is called whenever the hash changes
    const handler = () => setLoc(currentLocation());

    // subscribe to hash changes
    window.addEventListener("hashchange", handler);
    return () => window.removeEventListener("hashchange", handler);
  }, []);

  return [loc, navigate];
};

const App = () => (
  <Router hook={useHashLocation}>
    <Route path="/about" component={About} />
    ...
  </Router>
);

β–Ά Demo Sandbox: hash-based routing

useRouter: accessing the router object

If you're building an advanced integration, for example custom location hook, you might want to get access to the global router object. The router is a simple object that holds current matcher function and a custom location hook function.

Normally, router is constructed internally on demand, but it can also be customized via a top-level Router component (see the section above). The useRouter hook simply returns a current router object:

import { useRouter } from "wouter";
import useLocation from "wouter/use-location";

const Custom = () => {
  const router = useRouter();

  // router.hook is useLocation by default

  // you can also use router as a mediator object
  // and store arbitrary data on it:
  router.lastTransition = { path: "..." };
};

Component API

<Route path={pattern} />

Route represents a piece of the app that is rendered conditionally based on a pattern. Pattern is a string, which may contain special characters to describe dynamic segments, see Matching Dynamic Segments section below for details.

The library provides multiple ways to declare a route's body:

import { Route } from "wouter";

// simple form
<Route path="/home"><Home /></Route>

// render-prop style
<Route path="/users/:id">
  {params => <UserPage id={params.id} />}
</Route>

// the `params` prop will be passed down to <Orders />
<Route path="/orders/:status" component={Orders} />

<Link href={path} />

Link component renders an <a /> element that, when clicked, performs a navigation. You can customize the link appearance by providing your own component or a link element as children:

import { Link } from "wouter"

// All of these will produce the same html:
// <a href="/foo" class="active">Hello!</a>

// lazy form: `a` element is constructed around children
<Link href="/foo" className="active">Hello!</Link>

// when using your own component or jsx the `href` prop
// will be passed down to an element
<Link href="/foo"><a className="active">Hello!</a></Link>
<Link href="/foo"><A>Hello!</A></Link>

If you wrap a custom component with Link, wouter won't install event listeners so make sure the component handles onClick and href props properly:

import { Link } from "wouter";

const MyButton = (props) => {
  // it is recommended to use <a>'s when possible (they play nicely with SSR and are SEO-friendly),
  // but wouter's Links should work with almost anything, as long as the `onClick` is handled.
  return (
    <div title={props.href}>
      <button onClick={props.onClick}>Home</button>
    </div>
  );
};

// in your app
<Link href="/home">
  <MyButton />
</Link>;

<Switch />

There are cases when you want to have an exclusive routing: to make sure that only one route is rendered at the time, even if the routes have patterns that overlap. That's what Switch does: it only renders the first matching route.

import { Route, Switch } from "wouter";

<Switch>
  <Route path="/orders/all" component={AllOrders} />
  <Route path="/orders/:status" component={Orders} />
</Switch>;

Check out FAQ and Code Recipes section for more advanced use of Switch.

<Redirect to={path} />

When mounted performs a redirect to a path provided. Uses useLocation hook internally to trigger the navigation inside of a useEffect block.

If you need more advanced logic for navigation, for example, to trigger the redirect inside of an event handler, consider using useLocation hook instead:

import { useLocation } from "wouter";

const [location, setLocation] = useLocation();

fetchOrders().then((orders) => {
  setOrders(orders);
  setLocation("/app/orders");
});

<Router hook={hook} matcher={matchFn} base={basepath} />

Unlike React Router, routes in wouter don't have to be wrapped in a top-level component. An internal router object will be constructed on demand, so you can start writing your app without polluting it with a cascade of top-level providers. There are cases however, when the routing behaviour needs to be customized.

These cases include hash-based routing, basepath support, custom matcher function etc.

A router is a simple object that holds the routing configuration options. You can always obtain this object using a useRouter hook. The list of currently available options:

  • hook: () => [location: string, setLocation: fn] β€” is a React Hook function that subscribes to location changes. It returns a pair of current location string e.g. /app/users and a setLocation function for navigation. You can use this hook from any component of your app by calling useLocation() hook.

Read more β†’ Customizing the location hook.

  • matcher: (pattern: string, path: string) => [match: boolean, params: object] β€” a custom function used for matching the current location against the user-defined patterns like /app/users/:id. Should return a match result and an hash of extracted parameters. It should return [false, null] when there is no match.

  • base: string β€” an optional setting that allows to specify a base path, such as /app. All application routes will be relative to that path. Prefixing a route with ~ will make it absolute, bypassing the base path.

Matching Dynamic Segments

Just like in React Router, you can make dynamic matches either with Route component or useRoute hook. useRoute returns a second parameter which is a hash of all dynamic segments matched. Similarily, the Route component passes these parameters down to its children via a function prop.

import { useRoute } from "wouter";

// /users/alex => [true, { name: "alex "}]
// /anything   => [false, null]
const [match, params] = useRoute("/users/:name");

// or with Route component
<Route path="/users/:name">
  {(params) => {
    /* { name: "alex" } */
  }}
</Route>;

wouter implements a limited subset of path-to-regexp package used by React Router or Express, and it supports the following patterns:

  • Named dynamic segments: /users/:foo.
  • Dynamic segments with modifiers: /foo/:bar*, /foo/baz? or /foo/bar+.

The library was designed to be as small as possible, so most of the additional matching features were left out (see this issue for more info).

Using a path-to-regexp-based matcher

The <Router /> component accepts an optional prop called matcher which allows to customize how a path is matched against the pattern. By default, a built-in matcher function is used, which implements basic functionality such as wildcard parameters (see above).

However, if you do need to have more advanced functionality, you can specify your own matcher which should look like:

/*
 * accepts a pattern and a path as strings, should return a pair of values:
 * [success, params]
 */

// returns [false, null] when there is no match
matcher("/users", "/") 

// [true, { id: "101" }]
matcher("/users/:id", "/users/101") 

Most of the packages for parsing route patterns work with regular expressions (see path-to-regexp or a super-tiny alternative regexparam), so to make it easier for you wouter provides a factory function for transforming a regexp-based pattern builder into a matcher. It also makes sure that the expensive transform operation isn't called on each render by utilizing a simple cache.

import { Router } from "wouter";

import makeCachedMatcher from "wouter/matcher";

/*
 * This function specifies how strings like /app/:users/:items* are
 * transformed into regular expressions.
 *
 * Note: it is just a wrapper around `pathToRegexp`, which uses a
 * slighly different convetion of returning the array of keys.
 *
 * @param {string} path β€” a path like "/:foo/:bar"
 * @return {{ keys: [], regexp: RegExp }}
 */
const convertPathToRegexp = (path) => {
  let keys = [];

  // we use original pathToRegexp package here with keys
  const regexp = pathToRegexp(path, keys, { strict: true });
  return { keys, regexp };
};

const customMatcher = makeCachedMatcher(convertPathToRegexp);

function App() {
  return (
    <Router matcher={customMatcher}>
      {/* at the moment wouter doesn't support inline regexps, but path-to-regexp does! */}
      <Route path="/(resumes|cover-letters)/:id" component={Dashboard} />
    </Router>
  )
}

β–Ά Demo Sandbox

FAQ and Code Recipes

I deploy my app to the subfolder. Can I specify a base path?

You can! Wrap your app with <Router base="/app" /> component and that should do the trick:

import { Router, Route, Link } from "wouter";

const App = () => (
  <Router base="/app">
    {/* the link's href attribute will be "/app/users" */}
    <Link href="/users">Users</Link>

    <Route path="/users">The current path is /app/users!</Route>
  </Router>
);

Note: the base path feature is only supported by the default pushState hook. If you're implementing your own location hook, you'll need to add base path support yourself.

How do I make a default route?

One of the common patterns in application routing is having a default route that will be shown as a fallback, in case no other route matches (for example, if you need to render 404 message). In wouter this can easily be done as a combination of <Switch /> component and a default route:

import { Switch, Route } from "wouter";

<Switch>
  <Route path="/about">...</Route>
  <Route>404, Not Found!</Route>
</Switch>;

Note: the order of switch children matters, default route should always come last. If you want to have access to the matched segment of the path you can use :param*:

<Switch>
  <Route path="/users">...</Route>

  {/* will match anything that starts with /users/, e.g. /users/foo, /users/1/edit etc. */}
  <Route path="/users/:rest*">...</Route>

  {/* will match everything else */}
  <Route path="/:rest*">
    {(params) => `404, Sorry the page ${params.rest} does not exist!`}
  </Route>
</Switch>

β–Ά Demo Sandbox

How do I make a link active for the current route?

There are cases when you need to highlight an active link, for example, in the navigation bar. While this functionality isn't provided out-of-the-box, you can easily write your own <Link /> wrapper and detect if the path is active by using the useRoute hook. The useRoute(pattern) hook returns a pair of [match, params], where match is a boolean value that tells if the pattern matches current location:

const [isActive] = useRoute(props.href);

return (
  <Link {...props}>
    <a className={isActive ? "active" : ""}>{props.children}</a>
  </Link>
);

β–Ά Demo Sandbox

Are strict routes supported?

If a trailing slash is important for your app's routing, you could specify a custom matcher that implements the strict option support.

import makeMatcher from "wouter/matcher";
import { pathToRegexp } from "path-to-regexp";

const customMatcher = makeMatcher((path) => {
  let keys = [];
  const regexp = pathToRegexp(path, keys, { strict: true });
  return { keys, regexp };
});

const App = () => (
  <Router matcher={customMatcher}>
    <Route path="/foo">...</Route>
    <Route path="/foo/">...</Route>
  </Router>
);

β–Ά Demo Sandbox

Are relative routes and links supported?

Unlike React Router, there is no first-class support for route nesting. However, thanks to the base path support, you can easily implement a nesting router yourself!

const NestedRoutes = (props) => {
  const router = useRouter();
  const [parentLocation] = useLocation();

  const nestedBase = `${router.base}${props.base}`;

  // don't render anything outside of the scope
  if (!parentLocation.startsWith(nestedBase)) return null;

  // we need key to make sure the router will remount when base changed
  return (
    <Router base={nestedBase} key={nestedBase}>
      {props.children}
    </Router>
  );
};

const App = () => (
  <Router base="/app">
    <NestedRoutes base="/dashboard">
      {/* the real url is /app/dashboard/users */}
      <Link to="/users" />
      <Route path="/users" />
    </NestedRoutes>
  </Router>
);

β–Ά Demo Sandbox

Is it possible to match an array of paths?

While wouter doesn't currently support multipath routes, you can achieve that in your app by specifying a custom matcher function:

import makeMatcher from "wouter/matcher";

const defaultMatcher = makeMatcher();

/*
 * A custom routing matcher function that supports multipath routes
 */
const multipathMatcher = (patterns, path) => {
  for (let pattern of [patterns].flat()) {
    const [match, params] = defaultMatcher(pattern, path);
    if (match) return [match, params];
  }

  return [false, null];
};

const App = () => (
  <Router matcher={multipathMatcher}>
    <Route path={["/app", "/home"]}>...</Route>
  </Router>
);

β–Ά Demo Sandbox

Can I use wouter in my TypeScript project?

Yes! Although the project isn't written in TypeScript, the type definition files are bundled with the package.

Preact support?

Preact exports are available through a separate package named wouter-preact (or within the wouter/preact namespace, however this method isn't recommended as it requires React as a peer dependency):

- import { useRoute, Route, Switch } from "wouter";
+ import { useRoute, Route, Switch } from "wouter-preact";

You might need to ensure you have the latest version of Preact X with support for hooks.

β–Ά Demo Sandbox

Is there any support for server-side rendering (SSR)?

Yes! In order to render your app on a server, you'll need to tell the router that the current location comes from the request rather than the browser history. In wouter, you can achieve that by replacing the default useLocation hook with a static one:

import { renderToString } from "react-dom/server";
import { Router } from "wouter";

// note: static location has a different import path,
// this helps to keep the wouter source as small as possible
import staticLocationHook from "wouter/static-location";

import App from "./app";

const handleRequest = (req, res) => {
  // The staticLocationHook function creates a hook that always
  // responds with a path provided
  const prerendered = renderToString(
    <Router hook={staticLocationHook(req.path)}>
      <App />
    </Router>
  );

  // respond with prerendered html
};

Make sure you replace the static hook with the real one when you hydrate your app on a client.

If you want to be able to detect redirects you can provide the record option:

import { renderToString } from "react-dom/server";
import { Router } from "wouter";
import staticLocationHook from "wouter/static-location";

import App from "./app";

const handleRequest = (req, res) => {
  const location = staticLocationHook(req.path, { record: true });
  const prerendered = renderToString(
    <Router hook={location}>
      <App />
    </Router>
  );

  // location.history is an array matching the history a
  // user's browser would capture after loading the page

  const finalPage = locationHook.history.slice(-1)[0];
  if (finalPage !== req.path) {
    // perform redirect
  }
};

1KB is too much, I can't afford it!

We've got some great news for you! If you're a minimalist bundle-size nomad and you need a damn simple routing in your app, you can just use the useLocation hook which is only 400 bytes gzipped and manually match the current location with it:

import useLocation from "wouter/use-location";

const UsersRoute = () => {
  const [location] = useLocation();

  if (location !== "/users") return null;

  // render the route
};

Wouter's motto is "Minimalist-friendly".

Acknowledgements

Wouter illustrations and logos were made by Katya Simacheva and Katya Vakulenko.

Comments
  • Wouter isn't compatible with TypeScript and Preact at the same time

    Wouter isn't compatible with TypeScript and Preact at the same time

    Wouter can't be used with TypeScript and Preact at the same time.

    If you install @types/wouter, that also installs @types/react, which conflicts with and breaks type definitions for preact.

    It (probably) works if you use preact-compat, but at that point you're not really "compatible with preact" in my opinion.

    A possible fix would be to push an alternative @types/wouter-preact that worked with Preact's type definitions.

    CC @StrayFromThePath - Thought this would be relevant for you.

    opened by Hubro 17
  • Add TypeScript type definitions to wouter-types repo, publish to definitely-typed

    Add TypeScript type definitions to wouter-types repo, publish to definitely-typed

    Hello,

    After 4 years of using React, I finally see a reasonable react-router.

    There's just one thing I'd appreciate.

    Could you please provide typescript type definitions? Either include it in this package, or add them to definitely-typed repo.

    Thank you!

    help wanted 
    opened by matuscongrady 17
  • Discussion about default routes

    Discussion about default routes

    i wrote a demo:

    <Switch>
        <Route path="/:anything*">Default Route: nothing found!</Route>
        <Route path="/users/one">Users One</Route>
        <Route path="/">First Route</Route>
         //...
        {/* <Route path="/:anything*">Default Route: nothing found!</Route> */}
    </Switch>
    

    If we accidentally put the default route in front, it will cause problems.

    I think there are two ways:

    1. The problem is explained in the documentation, which draws the attention of developers⚠️
    2. If you use <Route default component={AnyComponent}/> and the position is irrelevant, is it a good idea?

    @omgovich @molefrog Looking forward to your reply.

    opened by cbbfcd 14
  • Incorrect Link href with basepath

    Incorrect Link href with basepath

    Hello, I'm trying to implement basepath into my application. I followed the README's instruction. Everything works well, except <Link /> component.

    For example,

    <Link href="/path">link</Link>
    

    It's fine that clicking on it will navigate to :basepath/path, but the href prop is still /path.

    Now I had to use <Link /> in this way,

    <Link href="/path">
      <span><a href={`${basepath}/path`}>link</a></span>
    </Link>
    

    Any idea on it?

    opened by giuem 13
  • Many exports not found

    Many exports not found

    I'm trying to use wouter in place of react-router. I'm having problems soon as I import anything from the wouter package. I get 18 errors that look like the below. I've reproduced a minimum working example in [this repository] (https://github.com/johnrichardrinehart/wouter). To reproduce locally it should be sufficient to git clone that repository and then yarn run. Please note that I'm using yarn berry. So, a hopefully comprehensive set of instructions to reproduce would be:

    1. git clone https://github.com/johnrichardrinehart/wouter
    2. yarn set version berry (assuming yarn is installed already)
    3. yarn install (dependencies are cached in my repo, but it's good to make sure)
    4. yarn run build (look at all the errors... should look like below)

    Please let me know what I can do to help us debug this issue.

    ERROR in ./.yarn/$$virtual/wouter-virtual-21dacc859c/0/cache/wouter-npm-2.6.0-237130b851-d80392b074.zip/node_modules/wouter/index.js 23:18-31
    export 'createContext' (imported as 'createContext') was not found in './react-deps.js' (possible exports: )
     @ ./src/index.js 4:0-31 7:42-47
    
    ERROR in ./.yarn/$$virtual/wouter-virtual-21dacc859c/0/cache/wouter-npm-2.6.0-237130b851-d80392b074.zip/node_modules/wouter/index.js 32:20-30
    export 'useContext' (imported as 'useContext') was not found in './react-deps.js' (possible exports: )
     @ ./src/index.js 4:0-31 7:42-47
    
    ERROR in ./.yarn/$$virtual/wouter-virtual-21dacc859c/0/cache/wouter-npm-2.6.0-237130b851-d80392b074.zip/node_modules/wouter/index.js 51:17-23
    export 'useRef' (imported as 'useRef') was not found in './react-deps.js' (possible exports: )
     @ ./src/index.js 4:0-31 7:42-47
    
    ERROR in ./.yarn/$$virtual/wouter-virtual-21dacc859c/0/cache/wouter-npm-2.6.0-237130b851-d80392b074.zip/node_modules/wouter/index.js 63:14-20
    export 'useRef' (imported as 'useRef') was not found in './react-deps.js' (possible exports: )
     @ ./src/index.js 4:0-31 7:42-47
    
    ERROR in ./.yarn/$$virtual/wouter-virtual-21dacc859c/0/cache/wouter-npm-2.6.0-237130b851-d80392b074.zip/node_modules/wouter/index.js 70:9-10
    export 'createElement' (imported as 'h') was not found in './react-deps.js' (possible exports: )
     @ ./src/index.js 4:0-31 7:42-47
    
    ERROR in ./.yarn/$$virtual/wouter-virtual-21dacc859c/0/cache/wouter-npm-2.6.0-237130b851-d80392b074.zip/node_modules/wouter/index.js 85:24-25
    export 'createElement' (imported as 'h') was not found in './react-deps.js' (possible exports: )
     @ ./src/index.js 4:0-31 7:42-47
    
    ERROR in ./.yarn/$$virtual/wouter-virtual-21dacc859c/0/cache/wouter-npm-2.6.0-237130b851-d80392b074.zip/node_modules/wouter/index.js 97:22-33
    export 'useCallback' (imported as 'useCallback') was not found in './react-deps.js' (possible exports: )
     @ ./src/index.js 4:0-31 7:42-47
    
    ERROR in ./.yarn/$$virtual/wouter-virtual-21dacc859c/0/cache/wouter-npm-2.6.0-237130b851-d80392b074.zip/node_modules/wouter/index.js 121:14-28
    export 'isValidElement' (imported as 'isValidElement') was not found in './react-deps.js' (possible exports: )
     @ ./src/index.js 4:0-31 7:42-47
    
    ERROR in ./.yarn/$$virtual/wouter-virtual-21dacc859c/0/cache/wouter-npm-2.6.0-237130b851-d80392b074.zip/node_modules/wouter/index.js 121:52-53
    export 'createElement' (imported as 'h') was not found in './react-deps.js' (possible exports: )
     @ ./src/index.js 4:0-31 7:42-47
    
    ERROR in ./.yarn/$$virtual/wouter-virtual-21dacc859c/0/cache/wouter-npm-2.6.0-237130b851-d80392b074.zip/node_modules/wouter/index.js 123:9-21
    export 'cloneElement' (imported as 'cloneElement') was not found in './react-deps.js' (possible exports: )
     @ ./src/index.js 4:0-31 7:42-47
    
    ERROR in ./.yarn/$$virtual/wouter-virtual-21dacc859c/0/cache/wouter-npm-2.6.0-237130b851-d80392b074.zip/node_modules/wouter/index.js 136:6-20
    export 'isValidElement' (imported as 'isValidElement') was not found in './react-deps.js' (possible exports: )
     @ ./src/index.js 4:0-31 7:42-47
    
    ERROR in ./.yarn/$$virtual/wouter-virtual-21dacc859c/0/cache/wouter-npm-2.6.0-237130b851-d80392b074.zip/node_modules/wouter/index.js 145:13-25
    export 'cloneElement' (imported as 'cloneElement') was not found in './react-deps.js' (possible exports: )
     @ ./src/index.js 4:0-31 7:42-47
    
    ERROR in ./.yarn/$$virtual/wouter-virtual-21dacc859c/0/cache/wouter-npm-2.6.0-237130b851-d80392b074.zip/node_modules/wouter/index.js 155:2-17
    export 'useLayoutEffect' (imported as 'useLayoutEffect') was not found in './react-deps.js' (possible exports: )
     @ ./src/index.js 4:0-31 7:42-47
    
    ERROR in ./.yarn/$$virtual/wouter-virtual-21dacc859c/0/cache/wouter-npm-2.6.0-237130b851-d80392b074.zip/node_modules/wouter/react-deps.js 1:0-12:15
    Module not found: Error: Can't resolve 'react' in '/home/john/Downloads/repos/temp/.yarn/$$virtual/wouter-virtual-21dacc859c/0/cache/wouter-npm-2.6.0-237130b851-d80392b074.zip/node_modules/wouter'
    Did you mean 'index.js'?
    BREAKING CHANGE: The request 'react' failed to resolve only because it was resolved as fully specified
    (probably because the origin is a '*.mjs' file or a '*.js' file where the package.json contains '"type": "module"').
    The extension in the request is mandatory for it to be fully specified.
    Add the extension to the request.
     @ ./.yarn/$$virtual/wouter-virtual-21dacc859c/0/cache/wouter-npm-2.6.0-237130b851-d80392b074.zip/node_modules/wouter/index.js 4:0-13:25 23:18-31 32:20-30 51:17-23 63:14-20 70:9-10 85:24-25 97:22-33 121:14-28 121:52-53 123:9-21 136:6-20 145:13-25 155:2-17
     @ ./src/index.js 4:0-31 7:42-47
    
    ERROR in ./.yarn/$$virtual/wouter-virtual-21dacc859c/0/cache/wouter-npm-2.6.0-237130b851-d80392b074.zip/node_modules/wouter/use-location.js 12:25-33
    export 'useState' (imported as 'useState') was not found in './react-deps.js' (possible exports: )
     @ ./.yarn/$$virtual/wouter-virtual-21dacc859c/0/cache/wouter-npm-2.6.0-237130b851-d80392b074.zip/node_modules/wouter/index.js 1:0-45 26:9-21
     @ ./src/index.js 4:0-31 7:42-47
    
    ERROR in ./.yarn/$$virtual/wouter-virtual-21dacc859c/0/cache/wouter-npm-2.6.0-237130b851-d80392b074.zip/node_modules/wouter/use-location.js 13:19-25
    export 'useRef' (imported as 'useRef') was not found in './react-deps.js' (possible exports: )
     @ ./.yarn/$$virtual/wouter-virtual-21dacc859c/0/cache/wouter-npm-2.6.0-237130b851-d80392b074.zip/node_modules/wouter/index.js 1:0-45 26:9-21
     @ ./src/index.js 4:0-31 7:42-47
    
    ERROR in ./.yarn/$$virtual/wouter-virtual-21dacc859c/0/cache/wouter-npm-2.6.0-237130b851-d80392b074.zip/node_modules/wouter/use-location.js 15:2-11
    export 'useEffect' (imported as 'useEffect') was not found in './react-deps.js' (possible exports: )
     @ ./.yarn/$$virtual/wouter-virtual-21dacc859c/0/cache/wouter-npm-2.6.0-237130b851-d80392b074.zip/node_modules/wouter/index.js 1:0-45 26:9-21
     @ ./src/index.js 4:0-31 7:42-47
    
    ERROR in ./.yarn/$$virtual/wouter-virtual-21dacc859c/0/cache/wouter-npm-2.6.0-237130b851-d80392b074.zip/node_modules/wouter/use-location.js 40:19-30
    export 'useCallback' (imported as 'useCallback') was not found in './react-deps.js' (possible exports: )
     @ ./.yarn/$$virtual/wouter-virtual-21dacc859c/0/cache/wouter-npm-2.6.0-237130b851-d80392b074.zip/node_modules/wouter/index.js 1:0-45 26:9-21
     @ ./src/index.js 4:0-31 7:42-47
    
    webpack 5.9.0 compiled with 18 errors in 888 ms
    
    opened by johnrichardrinehart 12
  • [WIP] useNavigate (to not listen to history's state)

    [WIP] useNavigate (to not listen to history's state)

    Fixes #140 #142

    I haven't updated typings yet. I will do so later. Please let me know if there is something you would like to change in the PR.

    It's a breaking change, but brings a very important feature to have a stateless navigate method.

    Please be informed, this PR contains changes from #141 . Since that PR is approved, to make my life easier, I include changes from that branch also.

    opened by o-alexandrov 12
  • Router push with Switch

    Router push with Switch

    I made a <Switch> component like this:

    import { useRouter } from 'wouter'
    
    const Switch = ({ children }) => {
        const router = useRouter()
        const path = router.history.path()
        const childPaths = children.map(c => c.props.path)
        let matchedIndex
        childPaths.forEach((childPath, idx) => {
            const match = router.matcher(childPath, path)[0]
            if (isNaN(matchedIndex) && match) {
                matchedIndex = idx
            }
        })
        return children[matchedIndex]
    }
    
    export default Switch
    

    This just checks which of the children's path matches first and renders that Route. This sorta works, but when I navigate using router.history.push() afterwards, I get blank screens and nothing renders until I refresh. Any ideas?

    opened by nkov 11
  • Can wouter be used with NextJS?

    Can wouter be used with NextJS?

    Hi,

    I'm trying to use wouter with NextJS.

    The staticLocationHook returns a function so I can't serialize it and pass it as a prop using the provided code:

     const prerendered = renderToString(
        <Router hook={staticLocationHook(req.path)}>
          <App />
        </Router>
      );
    

    Any suggestions?

    Thanks in advance!

    opened by carlosagsmendes 10
  • Route

    Route "/" in scoped Router matches routes outside scope

    Hi!

    Thanks for Wouter, this is an amazing piece of hook-based technology :) So elegant yet so powerful.

    I've stumbled across an issue while using scoped Router (<Router base="/my/base/path" />)

    Basically, the problem is that inside such a Router, any path not starting with /my/base/path will still match the "/" path. For instance :

    function MyScopedApp() {
       return (
           <Router base="scope">
               <Switch>
                    <Route path="/subpage">    {/* Correctly only renders when path is `/scope/subpage'` */}
                         <SubPage />
                    </Route>
                    
                    <Route path="/">    {/* Incorrectly renders when path is `/outofscope/whatever` */}
                         <HomePage />
                    </Route>
               </Switch>
           </Router>
       )
    }
    

    Most of the time this would not be an issue, since MyScopedApp is most likely itself wrapped in a catch-all route scoped with the base path.

    But if "HomePage" for some reason manipulates the location, it will render once before being unmounted by its parent scoped Route. This causes an issue with the following setup:

    function MyScopedApp() {
       return (
           <Router base="/scope">
               <Switch>
                    <Route path="/subpage1">
                         <SubPage1 />
                    </Route>
                    <Route path="/subpage2">
                         <SubPage2 />
                    </Route>
                    
                    { /* Redirect /scope to /scope/subpage1 */ }
                    <Route path="/">
                         <Redirect to="/subpage1" replace />
                    </Route>
               </Switch>
           </Router>
       )
    }
    

    What this wants to achieve is, when a user navigates to /scope, MyScopedApp is rendered, and automatically redirects to /scope/subpage1.

    The redirect on first navigation happens correctly, but the app is now soft-locked inside /scope/subpage1, because any attempt to navigate to /outofscope will match the Redirect's wrapping Route, thus triggering the Redirect itself.

    Additional information

    This behavior is seen across any match-related situation. For instance:

    // Inside a scoped router on "/scope", when pathname is currently "/outofscope"
    const [location] = useLocation()  
    console.log(location) // "/", should be null/undefined most likely? Or even "../outofscope"
    
    const [match] = useMatch("/")
    console.log(match) // true, should be false
    

    I guess this could be fixed by using a custom matcher on my scoping Router, but I feel like this behavior is somewhat not what other users would expect either, so maybe it should be fixed in the default matcher directly.

    opened by jvdsande 10
  • Package is not compatible with Create React App and it's testing setup

    Package is not compatible with Create React App and it's testing setup

    I see the published package contains import statements. This produce the following error in default create react app setup after running npm test

    Test suite failed to run
    
        /home/igor/react-hooks-beer-example/node_modules/wouter/index.js:1
        ({"Object.<anonymous>":function(module,exports,require,__dirname,__filename,global,jest){import locationHook from "./use-location.js";
                                                                                                        ^^^^^^^^^^^^
    
        SyntaxError: Unexpected identifier
    

    Can we publish a transpiled version containing require statements so no babel transpilation would be required inside node_modules?

    opened by mucsi96 10
  • TypeScript: Infer params from path

    TypeScript: Infer params from path

    Closes #210

    This will probably be a breaking change for TS users who manually typed their path params, I'm not sure if that warrants a major πŸ€” Open to alternative approaches as well.

    opened by itsMapleLeaf 9
  • Question: HOC with Typescript

    Question: HOC with Typescript

    I want to use "useLocation" in my class component with typescript. So far i have gotten to this point with looking at the example at #135

    export type UseLoc = {
      useLoc: [
        string,
        (
          to: string,
          options?:
            | {
              replace?: boolean | undefined;
            }
            | undefined
        ) => void
      ];
    };
    
    export function withUseLocation<T>(Component: ComponentType<T>) {
      return (hocProps: T) => {
        const useLoc = useLocation();
    
        // eslint-disable-next-line react/jsx-props-no-spreading
        return <Component {...(hocProps as T)} useLoc={useLoc} />;
      };
    }
    
    
    
    // in class component
    const { useLoc } = this.props as IProps & UseLoc;
    // eslint-disable-next-line @typescript-eslint/no-unused-vars
    const [location, setLocation] = useLoc;
    setLocation(`/test`);
    

    This is the workaround im using right now im just wondering if there is any better way to do it without so much ugly syntax.

    opened by ev3nst 0
  • Proposal, Enhancement: Loaders

    Proposal, Enhancement: Loaders

    Hello, I would be really nice if wouter will also support async loaders like react-router and now @tanstack/react-router

    Proposal, close to react-router

    
    const loaderDataFunction = async (params): Promise<void> => {
      return await Promise.resolve(params.id);
    }
    
    <Route
      loader={loaderDataFunction}
    >
    
    const data = useLoaderData();
    

    Data prefetching is a good addition to any router and I think it will benefit all of the community. Let me know what you think.

    opened by PodaruDragos 2
  • query string with wouter

    query string with wouter

    I don't see an example on how to declare route for query strings like /search?keyword=wouter, all the examples using the /search/:keyword type of URL,

    can anybody give an example of the query string type of URL using wouter?

    query strings 
    opened by songwang 0
  • [Enhancement] - Possible Code Reduction

    [Enhancement] - Possible Code Reduction

    Background

    The flattenChildren method uses the reduce - map pattern on MDN Array.prototype.flat docs. MDN Array.prototype.flatMap is the same pattern but claims to be slightly faster and is less code.

    https://github.com/molefrog/wouter/blob/467d8b2fb4bea48d5f6686743a785f7c87a2e5ad/index.js#L134

    I took the time to change the function if your interested in this code change. Also flattenChildren was written twice, below solution writes it once.

    const flattenChildren = children => {
        return Array.isArray(children)
            ? children.flatMap(c => flattenChildren(c && c.type === Fragment ? c.props.children : c))
            : [children];
    };
    
    opened by abjfdi 3
  • [feature] memory router?

    [feature] memory router?

    MemoryRouter is a great way to work with routes in Tests, and in sub-apps (like iframes). I could only find static routing, so I wrote this one down. What do you think?

    import {
        ReactNode,
        useState,
        useCallback,
        createContext,
        useContext,
    } from "react";
    import  { Router, RouterProps, Path } from "wouter";
    
    type NavFunc = (to: Path, options?: { replace?: boolean }) => void;
    const MemoryLocationCtx = createContext<Path>("/");
    
    export function useMemoryHook(path: Path[] = ["/"], { maxHistory = 100 } = {}) {
        const [history, setHistory] = useState<Path[]>(path);
    
        let useLocationHook: { (): [Path, NavFunc]; history?: any };
        const navigate: NavFunc = useCallback(
            (to: Path, { replace }: { replace?: boolean } = {}) => {
                setHistory(history => {
                    const nextHistory = history.slice(0, maxHistory);
    
                    if (replace) nextHistory.pop();
                    nextHistory.push(to);
    
                    return nextHistory;
                });
            },
            [maxHistory]
        );
    
        useLocationHook = () => {
            const location = useContext(MemoryLocationCtx);
            return [location, navigate];
        };
        useLocationHook.history = history;
    
        return useLocationHook;
    }
    interface InMemoryRouterProps extends Partial<Omit<RouterProps, "hook">> {
        children: ReactNode;
        initialPath?: Path | Path[];
    }
    
    export function MemoryRouter({ initialPath, ...props }: InMemoryRouterProps) {
        const inMemHook = useMemoryHook(
            typeof initialPath === "string" ? [initialPath] : initialPath
        );
        const location = inMemHook.history.slice(-1).pop();
    
        return (
            <MemoryLocationCtx.Provider value={location}>
                <Router hook={inMemHook} {...props} />
            </MemoryLocationCtx.Provider>
        );
    }
    
    opened by KutnerUri 2
Releases(v2.8.1)
  • v2.8.1(Nov 5, 2022)

    • When route parameters can be inferred from the path, they now have a type of { readonly [k in string]: string | undefined }. This takes into account optional segments that can be undefined #262, thanks @HansBrende
    • Proper typings of Route children: it does not allow mixing render props and other elements together #263, thanks @HansBrende
    • Switch now has less strict type of its children to allow conditional rendering and avoid confusion https://github.com/molefrog/wouter/commit/8f943abc72abb89f35b3cd6d2a57781106cef1b5
    Source code(tar.gz)
    Source code(zip)
  • v2.8.0(Nov 2, 2022)

    • Feature: Route parameters are now automatically inferred in TypeScript. @itsMapleLeaf via #211
    • Feature: Link component will now forward ref to the underlying a element of custom component provided by user. @trymoto via #259
    • Optimization: use useState instead of useRef for values that must be initialized only once. @HelKyle via #252 and #251
    • Bugfix: Prevent Link navigation when event was cancelled. @Tomas2D via #239

    πŸ‡ΊπŸ‡¦ We stand with Ukraine Donate or spread the world to help Ukrainian soldiers and refugees

    Source code(tar.gz)
    Source code(zip)
  • v2.7.5(Sep 22, 2021)

    • Fix a bug with a Switch not being able to process falsy children @jacobbuck via https://github.com/molefrog/wouter/pull/212
    • Code size optimizations @cbbfcd via https://github.com/molefrog/wouter/pull/191 and https://github.com/molefrog/wouter/pull/186
    Source code(tar.gz)
    Source code(zip)
  • v2.7.4(Mar 10, 2021)

    • Fix incorrect type of the makeMatcher function, @shannonrothe via #182
    • Fix a bug with useLocation not triggering the update with query string is changed, @wuzzeb via #178
    Source code(tar.gz)
    Source code(zip)
  • v2.7.3(Feb 5, 2021)

    • Fixed a regression bug introduced in v2.7.2: a path returned from useLocation() included a query string value e.g. "/foo?bar=baz", while it should have been just "/foo". See #170 for details.
    Source code(tar.gz)
    Source code(zip)
  • v2.7.0(Feb 5, 2021)

    We're continuing to improve the library by keeping it small and simple, thanks to our wonderful contributors. First of all, it's the React 17 support: I'm sure many of you have already upgraded your project to the new version of React, which apparently "has no new features". Wouter now has proper peerDependencies that allow you to use it with React 17 thanks to @omgovich!

    Another important update is that it's now possible to access the routes outside of the base scope when using a base parameter:

    <Router base="/app">
      <Link href="/relative">Relative Link</Link>
      {/* 
          by prexing a path with tilda, you can reference a global route: when you click on this link 
          it navigates you to "/absolute", and not "/app/absolute" 
       */}
      <Link href="~/absolute">Absolute Link</Link>
    </Router>
    

    See https://github.com/molefrog/wouter/pull/153 for more info. Credits @jvdsande

    Source code(tar.gz)
    Source code(zip)
  • v2.5.2(Oct 22, 2020)

    This patch release contains a few important bug-fixes:

    • base parameter in Router is now case-insensitive. Thanks @Veranete @cbbfcd via #138
    • Fixed a "func.apply is not a function" error when using a custom matcher. @cbbfcd via #137
    • The list of events is now exported from the use-location.js module. @o-alexandrov via #129

    Happy wouting!

    Source code(tar.gz)
    Source code(zip)
  • v2.5.1(Jul 30, 2020)

    This release aims to fix a problem when a subpath module require("wouter/static-location") can't be properly required from Node.js, thanks @davidje13 #126

    Wouter now properly supports ESM and CommonJS environments and it can even work within .mjs scripts!

    Source code(tar.gz)
    Source code(zip)
  • v2.5.0(Jul 19, 2020)

    ✨ The new release brings up several important updates!

    • The new project logo, thanks to Katya Simacheva

    • Released in 2.5.1 Wouter now targets ESM Node modules and properly supports CJS submodules. #126 thanks @davidje13

    • Using a default route in a Switch just became easier: use <Route /> component with no props and it will match everything that didn't match regular routes. @cbbfcd via #103

    <Switch>
      <Route path="/users> ... </Route>
    - <Route path="/:rest*">
    + <Route>
        Not Found!
      </Route>
    </Switch>
    

    check out the Demo β†’

    • Static location hook now supports record: option, which allows to save the history of location changes in hook.history. It improves the testing DX and allows to detect redirects in SSR responses. #113, thanks @davidje13
    • Link and Redirect components now can accept replace option that will tell wouter that replaceState navigation should be used. Example:
    <Link to="/users" replace>Users</Link>
    <Redirect to="/home" replace />
    
    • Some tiny updates in the README: table of contents for better navigation, more examples, testimonials! It's such an honor to see that wouter's community is getting bigger and developers trust it in production: image

    • The entire type definition codebase had been reworked! Types are updated to support latest features like default routes and replace navigation, and to support custom location hooks that might have specific navigational options. This change is especially important in terms of wouter's new strategy: supporting new features through external extendability see https://github.com/molefrog/wouter/issues/102#issuecomment-654694679.

    This will allow us to easily implement custom location hooks, that can for example support state changes:

    import { useLocation } from "wouter";
    import useLocationWithState, { LocationWithState }  from '@wouter/use-location-state';
    
    const NavigateWithState = () => {
      // useLocation is a generic function that can accept the custom location hook type
      const [location, update] = useLocation<LocationWithState>();
      
      const onClick = useCallback(() => {
        // this is now a valid function call, `state:` option is included in LocationWithState type
        update("/home", state: { foo: "bar" });
      }, [update]);
    
      return null;
    }
    
    const App = () => (
      <Router hook={useLocationWithState}>
        <Link<LocationWithState> to="/app" state={{ foo: 'bar' }}>Click Me!</Link>
      </Router>
    );
    
    Source code(tar.gz)
    Source code(zip)
  • v2.4.0(Nov 19, 2019)

    Though the base path support was possible before, via a custom useLocation hook, it wasn't fully SEO-friendly β€” for example, the link would still have a relative href attribute.

    After a long discussion, it's been decided to enable the base path support on a top-level Router component πŸŽ‰ You can now just wrap your app with <Router base="/app"> and that's it! Learn more about this feature β†’

    The added bundle cost is just 48 extra bytes.

    @omgovich @cbbfcd @guiem via #76

    Source code(tar.gz)
    Source code(zip)
  • v2.3.0(Oct 10, 2019)

    • Wouter now is fully-compatible with Preact 10.0! You can install Preact version via npm install wouter-preact #88, thanks @developit
    • Type definitions now support generics, handy if you want to specify a shape of props your component accepts #87, thanks @ArnaudBarre
    • useLocation now also catches updates that may happens between the initial render and useEffect call. See #85
    • Redirect component now uses useLayoutEffect to trigger the update as soon as possible and prevent apps from flicker.
    Source code(tar.gz)
    Source code(zip)
  • v2.2.0(Aug 12, 2019)

    Improvements:

    • TypeScript types are now bundled with the library, including types for React and wouter/preact! Types are properly tested and can be maintained in the same repo. Thanks to @Ty3uK @StrayFromThePath and @polRk
    • Wouter now comes with a standalone package with Preact-only version: npm install wouter-preact. This makes it possible to use proper peerDependecies, and stop relying on React in your project #48 #49 cc/ @cedeber
    • 2nd argument of useLocation is now properly cached #74 #69 cc/ @neves
    • Links do not fire navigation when click using modifier keys like ctrl or ⌘ #68 thanks @marvinhagemeister
    • setLocation now supports a 2nd argument which allows to perform replaceState navigation (@Ty3uK via #52):
    setLocation('/about', true); // calls replaceState instead of pushState
    
    Source code(tar.gz)
    Source code(zip)
  • v2.1.1(Jul 8, 2019)

  • v2.1.0(Jun 28, 2019)

    Improvements:

    • Implicit routers are always created once #44
    • useLocation accepts router as an argument #44
    • Proper dependencies in useEffect, code only runs on mount/unmount #41 @jeetiss
    • Matcher refactoring #42 @jeetiss
    Source code(tar.gz)
    Source code(zip)
  • v1.3.2(May 30, 2019)

  • v1.3.0(May 27, 2019)

  • v1.2.3(May 13, 2019)

  • v1.2.2(May 13, 2019)

  • v1.2.1(May 13, 2019)

  • v1.2.0(Apr 29, 2019)

    • New <Redirect> and <Switch> components #7
    • Make the code ES6 compliant #10
    • Add onClick handler to <Link> component #12
    • Better support for SSR with extra/static-history.js history #6
    • A project logo by Katya Vakulenko
    Source code(tar.gz)
    Source code(zip)
Owner
Alexey Taktarov
React / Rails / Design / Products
Alexey Taktarov
Declarative routing for React

React Router Declarative routing for React Docs View the docs here Migrating from 2.x/3.x? 3.x docs 2.x docs Packages This repository is a monorepo th

React Training 49.2k Jan 3, 2023
🧭 Declarative, asynchronous routing for React.

Navi Declarative, asynchronous routing for React. Navi is a JavaScript library for declaratively mapping URLs to asynchronous content. It comes with:

Frontend Armory 2.1k Dec 23, 2022
reach - Next Generation Routing for React

Next Generation Routing for React Documentation Documentation Site You can also find the docs in the website directory. Community Join us on Spectrum

Reach 6.9k Jan 7, 2023
Declarative routing for React

React Router Declarative routing for React Docs View the docs here Migrating from 2.x/3.x? 3.x docs 2.x docs Packages This repository is a monorepo th

React Training 49.2k Dec 27, 2022
Declarative routing for React

Welcome to React Router Β· React Router is a lightweight, fully-featured routing library for the React JavaScript library. React Router runs everywhere

Remix 49.3k Jan 9, 2023
You can found the concept of react hooks, local storage, conditional rendering and react-router-dom.

Getting Started with Create React App This project was bootstrapped with Create React App. Available Scripts In the project directory, you can run: np

Tikaram Acharya 2 Jun 1, 2022
A simple and safe router for React and TypeScript.

A simple and safe router for React and TypeScript.

Mathieu Acthernoene 247 Dec 31, 2022
A simple and safe router for React and TypeScript.

@swan-io/chicane A simple and safe router for React and TypeScript. Design principles Typed routes: improving the DX, and making sure all your params

Swan 248 Jan 2, 2023
Render isomorphic React + React Router apps and components in Node

Render isomorphic React + React Router apps and components in Node

Sequence Media 3 Nov 1, 2022
named routes for react-router and your react application

react-router-namesake Example import { Switch } from "react-router"; import { BrowserRouter, Route, Link } from "react-router-dom"; import { Router as

Joe Hsu 6 Aug 12, 2021
Upschool-react-assignment - Dummy Amazon clone with Bootstrap and React

Dummy Amazon Clone with React - NozamA ?? In this assignment, I tried to build a

Merve Karabulut 12 May 13, 2022
a more intuitive way of defining private, public and common routes for react applications using react-router-dom v6

auth-react-router is a wrapper over react-router-dom v6 that provides a simple API for configuring public, private and common routes (React suspense r

Pasecinic Nichita 12 Dec 3, 2022
A custom React router that leverages the Web Animations API and CSS animations.

A custom React router that leverages the Web Animations API and CSS animations.

Nate Adams 28 Jan 18, 2021
React component to keep the scroll of the page and to restore it

For React Router V4/V5. React component to keep the scroll of the page and to restore it if the user clicks on the previous button of its browser You

Patrick Faramaz 27 Mar 19, 2022
React app for api practice and CRA eject.

Dionysus Era CRA React with MS on macOS |_____|| | DioMsTsxG | Hima Balde Compare the config files and project structure with my Og App to correct/edi

Dionysus 1 Dec 27, 2021
Generated file-based routes for React Location and Vite

Generouted Generated file-based routes for React Location and Vite Motivation I enjoyed working with file-based routing since started using it with Ne

Omar Elhawary 152 Dec 22, 2022
Route Sphere - Sync query parameters with a MobX store and React Router

Route Sphere - Sync query parameters with a MobX store and React Router

VocaDB Devgroup 1 May 21, 2022
Provides drilldown-style horizontal slide transitions between index and child routes

This is a simple component that provides drilldown-style horizontal slide transitions between index and child routes.

null 12 Feb 28, 2022
βš—οΈ Treat your `` components for better SEO and UX.

react-link-filter βš—οΈ Treat your <NavLink /> components for better SEO and UX. Opens all external links in a new tab/window with rel="nofollow noopener

null 1 Jul 19, 2019