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 🙏

© 2026 – Pkg Stats / Ryan Hefner

effing-router

v0.4.13-0

Published

Config-based router with support of effector as first-class citizen

Downloads

39

Readme

Effing-router

Effing-router is a config-based router with support of effector as first-class citizen

  • Excellent integration with effector
  • Full Typescript support
  • ES modules and tree-shaking support
  • Modularity and customizations

How it works (or why another routing library)

effing-router utilizes different routing model than react-router.

Instead of parsing url and rendering routes that match path, effing-router offers routing by specifying routes (parts of your application) that you want to render, and url gets compiled out of routes as side-effect.

Getting started

import { router, initializeRouter } from 'effing-router';
import { bindDom } from 'effing-router/dom';
import { RouterView } from 'effing-router/react';

const Main = ({ childRoute }) => {
  return <div>
    <h1> Main </h1>
    <div>
      { childRoute() }
    </div>
  </div>
}

const Profile = () => {
  return <div>
    This is my profile
    <a onClick={() => router.go('news')}> Go to news </a>
  </div>
}

const News = () => {
  return <div>
    News
    <a onClick={() => router.go('profile')}> Go to profile </a>
  </div>
}

const Auth = () => {
  return <div> Signin </div>
}

const routes = [
  {
    name: 'main',
    component: Main,
    children: [
      {
        name: 'profile',
        component: Profile,
        path: '/myProfile'
      },
      {
        name: 'news',
        component: News,
        path: '/news'
      }
    ]
  },
  {
    name: 'auth',
    component: Auth
  }
]

initializeRouter(routes)
bindDom(router, routes)

// rendering routes
export const App = () => {
  return <RouterView
    router={router}
    routesList={routesList}
  />
}

Working with effector

Fetching data on route mount

import { forward, createEffect, restore } from 'effector';
import { router } from 'effing-router';

const fxGetStats = createEffect().use(statsApi);
const $stats = restore(fxGetStats, null);

forward({
  from: router.createMountEvent("dashboard"),
  to: fxGetStats
});

Demo: https://codesandbox.io/s/clever-firefly-89zp4?file=/src/features/Dashboard.tsx

Cleaning up on route unmount

import { createEvent, restore } from 'effector';
import { router } from 'effing-router';

const setData = createEvent<string[]>();
const $data = restore(setData, []);

const evDashboardUnmount = router.createUnmountEvent("dashboard");
$data.reset(evDashboardUnmount);

Demo: https://codesandbox.io/s/great-cache-jfnq9?file=/src/features/Dashboard.tsx

Working with params

import { router } from 'effing-router';

const $userId = router.createParamsStore('userId'),
$userId.watch(id => console.log('new user id: ', id))

router.go({ userId: 5 }); // 'new user id: 5'
router.go({ userId: 6 }); // 'new user id: 6'

Demo: https://codesandbox.io/s/determined-jennings-t6fjq?file=/src/features/Dashboard.tsx

Syncing store with url

import { restore, createEvent } from 'effector';
import { router } from 'effing-router';

const setPosition = createEvent();
const positionString = new URLSearchParams(location.search).get('position');
const $position = restore(setPosition, positionString ? JSON.parse(positionString) : { x: 0, y: 0 });

// router.go, router.replace are usual effector events
forward({
  from: $position,
  to: router.replace.prepend((position) => ({ position: JSON.stringify(position) }))
})

Demo: https://codesandbox.io/s/hungry-pare-lrsvx?file=/src/features/Dashboard.tsx

Core API

router

Main instance of router, it is a collection of events and functions that perform routing. Router state consists of two main part: routes and params

{
  routes: ['main', 'users'],
  params: { userId: 5 }
}

Routes specify what you want to render, i.e. what parts of your application, which are identified by their respective name on routes list.

Params specifty how you want to render, it can contain any data related to route state. You can persist any application data in params.

initializeRouter(router, routesList)

Accepts router object and list of routes configuration

Route configuration shape

{
  name: 'main',
  component: Main,
  children: [
    {
      name: 'news',
      component: News,
      path: '/news/:newsId'
    },
    {
      name: 'users',
      component: Users,
      redirect: {
        condition: $noUsers,
        to: 'news'
      }
    }
  ]
}

Only required prop is name. However, if you use RouterView from effing-router/react, you will have to specify component

Redirects

Redirect configuration can be specified for each route, it consists of two required properties: condition and to.

condition is a boolean store, and if has value true, redirects to to. It is reactive, which means redirect can be triggered both on router.go and when condition store becomes true

to can be any form of argument of router.go, basically router.go gets called with to

Compiling path

path is used to compile url. When specifiying route, the grandest child's path will be used, parents path will be ignored

// state
{
  routes: ['main', 'news'],
  params: { newsId: 7 }
}

/*
* router will look for 'news' route's path,
* because it is present, path will be compiled to
* 
* /news/7
*/

If not specified, path will be compiled from route names concatenated with / as delimeter, all params will be compiled with URLSearchParams.

// state
{
  routes: ['main', 'users'],
  params: { userId: 5 }
}

/*
* there is not path specified for 'users',
* by default it will be compiled to
*
* /main/users?userId=5
*/

router.go

Used to navigate to new route

import { router } from 'effing-router';

// relative routing, changes between siblings
// ['main', 'news'] => ['main', 'music']
router.go('music');

// absolute routing, does not change params
router.go(['main', 'music']);

// routing based on previous route
router.go(prevRoute => {
  routes: [...prevRouter.routes, 'main', 'music'],
  params: {
    ...prevRouter.params
    userCount: prevRouter.params.userCount + 1
  }
});

// writes new key-value params, overwrites if exists
router.go({ userId: 5 });

You can find more overloads in docs.

router.replace

It has same signatures as go, but instead of pushing new entry into history stack, it replaces last

router.createMountEvent

Creates event that is triggered when particular route (optionally with particular params) is visited.

Can be useful to perform on mount logic

router.createMountEvent(['main', 'music']) // triggered when router.go(['main', 'music'])

router.createMountEvent({
  routes: ['main', 'music'],
  params: { musicId: 5 }
}) // triggered only when visited with particular params

router.createUnmountEvent

It has same signatures as createMountEvent, but gets triggered when you leave particular route (optionally with particular params)

router.createParamsStore

Create store that holds value of particular param, gets updated when that param is changed

const $userId = createParamsStore('userId');

See Working with params

DOM API

DOM API is separated from core to its own module at effing-router/dom

bindDom(router, routesList, basename = '')

Accepts router instance. It will synchronize router state with browser url. It will have no effect in non-browser environments.

React API

API for rendering routes as React components, it's located at effing-router/react

<RouterView router routesList/>

Renders route tree

childRoute()

Each rendered component gets passed a childRoute function as prop, it is used to render child routes, and you can also pass additional props inside

const Main = ({ childRoute }) => {
  return <div>
    Main route
    { childRoute({ someExtraProp: 3 }) }
  </div>
}

useRouter()

Hook for accessing current router state. Also can be useful for conditional rendering

import { useRouter } from 'effing-router/react';

const Users = ({ childRoute }) => {
  const { path, params } = useRouter();

  return <div>
    User id: { params.userId }
    {path.includes('news') && <News/>}
  </div>
}

License

MIT