npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

woby-simple-router

v1.4.5

Published

A simple isomorphic router for Voby.

Downloads

8

Readme

Woby Simple Router

A simple isomorphic router for Woby.

Heavily inspired by solid-router. For a more direct port of that check out voby-router.

Install

pnpm install --save woby-simple-router

APIs

| Components | Hooks | Types | | ------------------------- | ------------------------------------- | --------------------------------------------- | | Router | useLoader | RouterLoader | | Route | useLocation | RouterLoaderContext | | Link | useNavigate | RouterLocation | | Navigate | useParams | RouterParams | | | useRoute | RouterPath | | | useRouter | RouterRoute | | | useSearchParams | RouterRouter |

Usage

The following functions are provided, divided between components and hooks.

Components

The following components are some building blocks for your router-aware interface.

Router

The Router is the most important component, every other component and hook must be called inside a Router component to work.

import Layout from './pages/_layout';
import Home from './pages/home';
import NotFound from './pages/not_found';
import UserAdmin from './pages/user_admin';
import UserProfile from './pages/user_profile';
import someLoader from './loaders/some';
import {lazy} from 'woby';
import {Navigate, Router} from 'woby-simple-router';

// First of all let's define some routes
// The router is based on Noren (https://github.com/fabiospampinato/noren)

const Routes = {
  { // A regular route, mapping a path to a component
    path: '/',
    to: Home
  },
  { // A redirection route, using the <Navigate> component
    path: '/redirect',
    to: <Navigate to="/" />
  },
  { // A route with children routes
    path: '/user',
    to: UserAdmin
    children: [
      { // A child route with a parameter
        path: ':username',
        to: UserProfile
      }
    ]
  },
  { // A route can also be lazy, for bundle-splitting purposes
    path: '/some',
    to: lazy ( () => import ( './pages/some' ) ),
    // A loader can be specified for each route, which returns some data
    // A loader allows for loading in parallel both the route and its data
    // The data will be accessible using the "useLoader" hook
    // Router hooks can't be called inside a loader, so a context object with some data is provided
    loader: ({ params }) => {
      return someLoader ();
    }
  },
  { // A route with a special path, which is matched when no other route matches
    path: '/404',
    to: NotFound
  }
};

// Then let's instantiate our router
// The path to render is not provided, so it's inferred, this is useful when running in the client

const app = (
  <Router routes={Routes}>
    <Layout />
  </Router>
);

// Now let's instantiate a router with a specific path
// The path is provided explicitly, this is mostly useful when doing SSR

const app = (
  <Router routes={Routes} path="/user/John">
    <Layout />
  </Router>
);

// Explicitly use the "path" backend, which is the default

const app = (
  <Router routes={Routes} backend="path" path="/user/John">
    <Layout />
  </Router>
);

// Explicitly use the "hash" backend, which stores the path in the hash part of the URL

const app = (
  <Router routes={Routes} backend="hash" path="/user/John">
    <Layout />
  </Router>
);

// Explicitly use the "memory" backend, which only stores the path in memory

const app = (
  <Router routes={Routes} backend="memory" path="/user/John">
    <Layout />
  </Router>
);

Route

This crucial component renders the actual route that the router selected. If you don't use it your route won'tbe rendered.

import {Route} from 'woby-simple-router';

// Left's render our route in the middle of some arbitrary components

const app = (
  <Router routes={Routes}>
    <Header />
    <Body>
      <Route />
    </Body>
    <Footer />
  </Router>
);

Link

This component provides an instant client-side navigation, without making a network request. It's basically an anchor tag that knows about the router.

import {Link} from 'woby-simple-router';

// Let's create a basic navbar

const Navbar = () => (
  <ul>
    <Link to="/">Homepage</Link>
    <Link to="/user">User</Link>
    <Link to="/about">About</Link>
    <Link to="/foo" replace>Foo+Replace</Link>
    <Link to="/foo" state={{ custom: 123 }}>Foo+State</Link>
  </ul>
);

Navigate

This component is like a Link that clicks itself right when it's rendered. It's useful for redirecting routes.

import {Navigate} from 'woby-simple-router';

// Let's redirect one route to another

const Routes = {
  {
    path: '/redirect',
    to: <Navigate to="/" />
  }
};

Hooks

The following hooks allow you to extract some information from the router.

useLoader

This hook gives you a resource to the resolved return value of the loader for the current route.

This hook is not type-safe, you should provide a type for the value as a generic type argument, but it won't be guaranteed to be correct.

import someLoader, {SomeValue} from './loaders/some';
import {lazy, Suspense} from 'woby';
import {useLoader} from 'woby-simple-router';

const Routes = [
  {
    path: '/some',
    to: lazy ( () => import ( './pages/some' ) ),
    loader: someLoader
  }
];

// Let's get the data from the loader

const App = () => {
  const loader = useLoader<SomeValue> ();
  const value = () => loader ().value;
  return (
    <Suspense fallback={<div>Still loading...</div>}>
      <p>Value: {value}</p>
    </Suspsense>
  );
};

useLocation

This hook tells you the pathname, search, and hash parts of the url the router is currently at.

import {useLocation} from 'woby-simple-router';

// Let's get the current location of the router

const App = () => {
  const {pathname, search, hash} = useLocation ();
  return (
    <>
      <p>Pathname: {pathname}</p>
      <p>Search: {search}</p>
      <p>Hash: {hash}</p>
    </>
  );
};

useNavigate

This hook allows you to change the pathname the router is at, programmatically.

import {useNavigate} from 'woby-simple-router';

// Let's build a custom <Link> component

const A = ({ href, children }) => {
  const navigate = useNavigate ();
  const onClick = event => {
    event.preventDefault ();
    console.log ( `Navigating to: "${href}"` );
    // Basic navigation, with history.pushState and no state value
    navigate ( href );
    // Replace navigation, with history.replaceState, and no state value
    navigate ( href, { replace: true } );
    // Replace navigation, with history.replaceState, and an arbitrary state value
    navigate ( href, { replace: true, state: {} } );
  };
  return <a href={href} onClick={onClick}>{children}</a>;
};

useParams

This hooks allows you to retrieve parameters defined in the currently matched path, e.g. /user/:username.

import {useParams} from 'woby-simple-router';

// Let's log a parameter

const App = () => {
  const params = useParams ();
  const username = () => params ().username;
  return <p>Current username: {username}</p>;
};

useRoute

This hooks gives you the currently matched route object, it's relatively low level, but powerful, because you can attach arbitrary data to a route and retriving it this way.

import {useRoute} from 'woby-simple-router';

// Let's log a parameter

const App = () => {
  const route = useRoute ();
  const someData = () => route ().someData;
  return <p>Some custom data: {someData}</p>;
};

useSearchParams

This hook allows you to read and write search parameters, which are those encoded in the URL after ?.

Currently the URLSearchParams you receive won't react to changes in search parameters unless the entire location changes.

import {useSearchParams} from 'woby-simple-router';

// Let's manipulate search parameters

const App = () => {
  const searchParams = useSearchParams ();
  const value = () => Number ( searchParams ().get ( 'value' ) ) || 0;
  const increment = () => searchparams ().set ( 'value', value () + 1 );
  return <p onClick={increment}>Current value: {value}</p>;
};

useRouter

This low-level hook gives you the raw router, given a list of routes. You might never have to use this directly.

import {useRouter} from 'woby-simple-router';

const Routes = {
  {
    path: '/',
    to: Home
  }
  // ...
};

// Creating the low-level router

const router = useRouter ( Routes );

// Trying to find a route given a path

router.route ( '/' ); // => { params: {}, route: { path: '/', to: Home } }
router.route ( '/missing' ); // => undefined

Types

The following types are provided.

RouterLoader

The type of a route's loader. The loader is called with a context object, since you can't call router hooks inside it.

type RouterLoader<T> = ( ctx: RouterLoaderContext ) => Promise<T>;

RouterLoaderContext

The context object passed as argument to each loader.

type RouterLoaderContext = {
  pathname: RouterPath,
  search: string,
  hash: string,
  params: RouterParams,
  searchParams: URLSearchParams,
  route: RouterRoute
};

RouterLocation

The type of the location object that useLocation gives you.

type RouterLocation = {
  pathname: ObservableReadonly<RouterPath>,
  search: ObservableReadonly<string>,
  hash: ObservableReadonly<string>
};

RouterParams

The type of the value that the read-only observable that useParams gives you contains.

type RouterParams = Record<string, string | undefined>;

RouterPath

The type for the allowed location. Basically it must always start with a /, for consistency.

type RouterPath = `/${string}`;

RouterRoute

The type of a route that you can pass to the router.

type RouterRoute = {
  path: string,
  to: JSX.Child,
  loader?: RouterLoader<unknown>,
  children?: RouterRoute[]
};

RouterRouter

The type of the low-level router, the one you get with useRouter.

type RouterRouter = {
  route ( path: string ): { params: RouterParams, route: RouterRoute } | undefined
};

License

MIT © Fabio Spampinato