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

@flemminghansen/react-outlet-router

v0.2.4

Published

Declarative client side router for React

Downloads

44

Readme

Disclaimer

Work in progress - Not ready for production - errors may occur

React Outlet Router

React Outlet Router is a declarative client-side router, built with maintainability in mind.

Why use React Outlet Router?

  • React Outlet Router is only 5kb minified and g-zipped.
  • Declarative, everything can be defined in a single object, which provides a clear overview of the entire app.
  • Flexible enough to use string matching, params and even regex patterns.
  • Typesafe - 100% written in TypeScript

Simple example

View more complex example here

import {
  RouterOutlet,
  useInitRouter,
  router,
} from "@flemminghansen/react-outlet-router";
import React from "react";
import ReactDOM from "react-dom/client";

function MockPage({ text }: {text: string}) {
    return <div>{text}</div>
}

function App() {
  const [isLoggedIn, setIsLoggedIn] = React.useState(false);

  // Router will be reinitialized on login. This way we can create a guard for the loggedin route.
  // For instance, the loggedin route and children won't be part of the routeTree until isLoggedIn is true.
  useInitRouter(
    {
    defaultPageTitle: "My simple router",
      default404Title: "404 - Page not found",
      defaultNotFoundComponent: () => <div>404 - Page not found</div>,
      fallback: <MockPage text="Please wait while we load your page dynamically..." />,
      routeTree: {
        "/": {
          component: MainPage,
        },
        "/loggedin": {
          removedIf: !isLoggedIn, 
          title: "Page one",
          component: () => <MockPage text="Welcome!!" />,
          children: {
            // loggedin/user
            "/user": { 
              title: "User page",
              component: () => <MockPage text="You are awesome!" />,
            },
          },
        },

        },
      },

    [isLoggedIn]
  );

  const handleLogin = () => {
    setIsLoggedIn(!isLoggedIn);

    if (!isLoggedIn) {
        router.replace('/loggedin')
    }
  }

  return (
    <>
        <button onClick={handleLogin}>
          {isLoggedIn ? "Log out" : "Log in"}
        </button>
      <RouterOutlet />
    </>
  );
}

const root = ReactDOM.createRoot(document.getElementById("app"));
root.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>
);

Installation

Installation is fairly simple using Node's package manager.

In your cli, run:

npm install @flemminghansen/react-outlet-router

What does React Outlet Router contain?

  • useInitRouter - A hook that allows you to initialize and reinitialize your router based on dependencies.
  • router - The Router object that's available after initialization. This will provide use with a range of utilities.
  • RouterOutlet - Where your best Route Match will be rendered.
  • Link component - Allows you to navigate React Outlet Router without reloading the entire page.
  • Redirect component - Can be used in the routeTree or as a component in your code to redirect your users on given criterias.

useInitRouter

useInitRouter takes one object with the following keys as defined by the interface IRemappedInitRouterProps.

interface IRemappedInitRouterProps {
  prefix?: string;
  defaultPageTitle: string;
  default404Title?: string;
  routeTree: Record<string, ILimitedRoute>; // key is the next part of the route, e.g. "/user"
  fallback: React.ReactNode;
  defaultNotFoundComponent: React.ComponentType<any>;
}

interface ILimitedRoute {
    component: React.ComponentType<any>;
    title?: string;
    fallback?: React.ReactNode;
    removedIf?: boolean;
    pattern?: RegExp;
    children?: Record<string, ILimitedRoute>;
}

defaultPageTitle(required)

Sets the document.title unless the routes specify something else

default404Title(optional)

Defaults to: "404 - Page not found"

defaultNotFoundComponent(required)

The component the router will render if there are no matches in the route tree.

fallback(required)

The RouterOuter output is wrapped in a React Suspense Component. The fallback is shown while a dynamically loaded page is loaded.

prefix(optional)

Defaults to "/"

A part that will we prefixed to the pathname. This can be used if your domain hold multiple apps that lives under prefixes, such as:

  • mydomain.com/myapp
  • mydomain.com/mysecondapp

Then you can prefix each app with "/myapp" or "mysecondapp", and each app will prefix every route with that prefix, thereby keeping the scope.

routeTree(required)

The routeTree is of type Record<string, ILimitedRoute>;, where the string is the path.

Each path must be prefixed with "/" e.g. "/user" or "/blog"

You can then use children to expand paths as in this example where the path to SettingsPage will be /user/settings.

        "/user": {
          title: "User Page",
          component: UserPage,
          children: {
            "/settings": { 
              title: "Settings page",
              component: SettingsPage,
            },
          },
        },

It's also possible to add params as a wildcard. These params can use used with the router.getParams method to allow the component to handle further rendering. But is can also be used to create custom 404 pages, or automatically redirect the user to a different path.

Currently redirect is done with the Redirect component, but this may be added as an option to the route in the future. See example:

        "/user": {
          title: "User Page",
          component: UserPage,
          children: {
            "/settings": { 
              title: "User settings",
              component: SettingsPage,
            },
            "/:invalid": { 
              component: () => <Redirect to="/user">, // This could also be a custom 404 page
            },
          },
        },

Properties

component: React.ComponentType - (required)

Sets the component that will be rendered at the RouterOutput if route is match.

pattern: RegExp - (optional)

Pattern overrides the given path for the route. It is recommended to use with a param e.g. /:userid, so that the value can be read with the router.getParams method.

        "/user": {
          component: ParentPage,
          children: {
            "/:userid": {
              // pattern will match integers like /1234
              // but not strings like /123s
              pattern: new RegExp(/^\d+$/),
              component: UserPageForId, 
            },
          },
        },

In the above example, the userid must be an integer in order to match the pattern.

  • /user/1234 == match
  • /user/1234s == nomatch

Then in the UserPageForId component we use the router.getParams method to get the value.

import React from "react";
import { router } from "@flemminghansen/react-outlet-router";

export function UserPageForId() {
  const { userid } = router.getParams();
  // Do magics with the userid which you know is an integer.
  ...
}

children: Record<string, ILimitedRoute> - (optional)

children is an object from which you can expand the route as described under routeTree

title: string - (optional)

Sets document.title for the given route

fallback: React.ComponentType - (optional)

Sets a fallback for the given route. If no component is passed, it defaults to the fallback you've set for the router.

work in progress

Route to 1.0

  1. Finish documentation.
  2. Get 100% test coverage and harden code.
  3. Clean up, and make it more readable.
  4. Optimize.
  5. JSDoc documentation in code for better developer experience.