
Research
/Security News
10 npm Typosquatted Packages Deploy Multi-Stage Credential Harvester
Socket researchers found 10 typosquatted npm packages that auto-run on install, show fake CAPTCHAs, fingerprint by IP, and deploy a credential stealer.
@cher-ami/router
Advanced tools
A fresh react router designed for flexible route transitions
cher-ami router API is inspired by wouter, solidify router and vue router API. This repository started from a copy of willybrauner/react-router.
Because managing route transitions with React is always complicated, this router is designed to allow flexible transitions. It provides Stack component who render previous and current page component when route change.
This router loads history , path-to-regexp and @wbe/debug as dependencies.
API
Components:
<Router /> Wrap Link and stack component<Link /> Trig current stack<Stack /> Wrap previous and current pageHooks:
useRouter Get current router informations like currentRoute and previousRouteuseLocation Get current location and set new locationuseStack Allow to the parent Stack to handle page transitions and refsuseRouteCounter Get global history route counteruseHistory Execute callback each time history changesuseLang get and set langService current language object
changesServices:
LangService Manage :lang paramsTranslate PathGlobal:
Helpers Global Routers helpersRouters object Global Routers object contains all routers properties (history, instances...)$ npm i @cher-ami/router -s
import React from "react";
import { Router, Link, Stack } from "@cher-ami/router";
import { createBrowserHistory } from "history";
const routesList = [
{
path: "/",
component: HomePage,
},
{
path: "/foo",
component: FooPage,
},
];
const history = createBrowserHistory();
function App() {
return (
<Router routes={routesList} history={history} base={"/"}>
<nav>
<Link to={"/"} />
<Link to={"/foo"} />
</nav>
<Stack />
</Router>
);
}
Page component need to be wrapped by React.forwardRef. The handleRef lets
hold transitions, and ref used by <Stack /> component.
import React from "react";
import { useStack } from "@cher-ami/router";
const FooPage = forwardRef((props, handleRef) => {
const componentName = "FooPage";
const rootRef = useRef(null);
// create custom page transitions (example with GSAP)
const playIn = () => {
return new Promise((resolve) => {
gsap.from(rootRef.current, { autoAlpha: 0, onComplete: resolve });
});
};
const playOut = () => {
return new Promise((resolve) => {
gsap.to(rootRef.current, { autoAlpha: 0, onComplete: resolve });
});
};
// register page transition properties used by Stack component
useStack({ componentName, handleRef, rootRef, playIn, playOut });
return (
<div className={componentName} ref={rootRef}>
{componentName}
</div>
);
});
Demo codesandbox: simple usage
cher-ami router use path-to-regexp which
accept path parameters. (check
this documentation).
For example, URL /blog/my-article will match with this route object:
const routesList = [
{
path: "/blog/:id",
component: ArticlePage,
},
];
You can access route parameters by page component props or by useRouter() hook.
import React, { useEffect, forwardRef } from "react";
import { useRoute } from "@cher-ami/router";
const ArticlePage = forwardRef((props, handleRef) => {
useEffect(() => {
console.log(props.params); // { id: "my-article" }
}, [props]);
// or from any nested components
const { currentRoute } = useRouter();
useEffect(() => {
console.log(currentRoute.props.params); // { id: "my-article" }
}, [currentRoute]);
// ...
});
Demo codesandbox: simple usage
Also, it is possible to match a specific route by a simple dynamic route
parameter for the "not found route" case. In this case, the routes object order
declaration is important. /:rest path route need to be the last of
the routesList array.
const routesList = [
{
path: "/",
component: HomePage,
},
{
path: "/foo",
component: FooPage,
},
// if "/" and "/foo" doesn't match with the current URL, this route will be rendered
{
path: "/:rest",
component: NotFoundPage,
},
];
Demo codesandbox: not found route
cher-ami router supports nested routes from sub routers instance ๐๐ฝ. It is possible to nest as many routers as you want.
children property;const routesList = [
{
path: "/",
component: HomePage,
},
{
path: "/foo",
component: FooPage,
// define children routes here
children: [
{
path: "/people",
component: PeoplePage,
},
{
path: "/yolo",
component: YoloPage,
},
],
},
];
Children were defined within the route that render FooPage component, so
you can then create a new router instance in this component.
The new subRouter needs his own base and routes list, getSubRouterBase and getSubRouterRoutes functions are available to get them.
import React from "react";
import {
Router,
useStack,
Stack,
useRouter,
getPathByRouteName,
getSubRouterBase,
getSubRouterRoutes,
} from "@cher-ami/router";
const FooPage = forwardRef((props, handleRef) => {
// Get parent router context
const { base, routes } = useRouter();
// Parsed routes list and get path by route name
const path = getPathByRouteName(routesList, "FooPage"); // "/foo"
// ...
return (
<div>
<Router
// -> "/base/:lang/foo" (if last param is false, ':lang' will be not added)
base={getSubRouterBase(path, base, true)}
// children routes array of FooPage
routes={getSubRouterRoutes(path, routes)}
>
<Stack />
</Router>
</div>
);
});
ManageTransitions function allows to define, "when" and "in what conditions",
routes transitions will be exectued.
By default, a "sequential" transitions senario is used by Stack component: the previous page play out performs, then the new page play in.
const sequencialTransition = ({ previousPage, currentPage, unmountPreviousPage }) => {
return new Promise(async (resolve) => {
const $current = currentPage?.$element;
// hide new page
if ($current) $current.style.visibility = "hidden";
// play out and unmount previous page
if (previousPage) {
await previousPage.playOut();
unmountPreviousPage();
}
// wait page isReady promise
await currentPage?.isReadyPromise?.();
// show and play in new page
if (currentPage) {
if ($current) $current.style.visibility = "visible";
await currentPage?.playIn();
}
resolve();
});
};
It's however possible to create a custom transitions senario function and pass
it to the Stack manageTransitions props. In this example, we would like to
create a "crossed" route senario: the previous page playOut performs at the same
time than the new page playIn.
const App = (props, handleRef) => {
const customSenario = ({ previousPage, currentPage, unmountPreviousPage }) => {
return new Promise(async (resolve) => {
// write a custom "crossed" senario...
if (previousPage) previousPage?.playOut();
if (currentPage) await currentPage?.playIn();
resolve();
});
};
return (
// ...
<Stack manageTransitions={customSenario} />
);
};
Demo codesandbox: custom manage transitions
This router is compatible with SSR due to using staticLocation props instead of history props on Router instance.
In this case, the router will match only with staticLocation props value and render the appropiate route without invoking the browser history. (Because window is not available on the server).
<Router
routes={routesList}
staticLocation={"/foo"}
// history={createBrowserHistory()}
>
// ...
</Router>
A use case example is available on this repos.
Install dependencies
$ npm i
Start dev server
$ npm run dev
Router component creates a new router instance.
<Router routes={} base={} history={} staticLocation={} middlewares={} id={}>
{/* can now use <Link /> and <Stack /> component */}
</Router>
Props:
TRoute[] Routes liststring Base URL - default: "/"BrowserHistory | HashHistory | MemoryHistory (optional) create and set an history - default : BrowserHistory
History mode can
be BROWSER
,
HASH
,
MEMORY
. For more information, check
the history library documentationstring (optional) use static URL location matching instead of history[] (optional) add routes middleware function to patch each routes)?number | string (optional) id of the router instance - default : 1Trig new route.
<Link to={} className={} />
Props:
string | TOpenRouteParams Path ex: /foo or {name: "FooPage" params: { id: bar }}.
"to" props accepts same params than setLocation.ReactNode children link DOM element()=> void (optional) execute callback on the click eventstring (optional) Class name added to component root DOM elementRender previous and current page component.
<Stack manageTransitions={} className={} />
Props:
(T:TManageTransitions) => Promise<void> (optional)
This function allows to create the transition scenario. If no props is filled,
a sequential transition will be executed.string (optional) className added to component root DOM
elementtype TManageTransitions = {
previousPage: IRouteStack;
currentPage: IRouteStack;
unmountPreviousPage: () => void;
};
interface IRouteStack {
componentName: string;
playIn: () => Promise<any>;
playOut: () => Promise<any>;
isReady: boolean;
$element: HTMLElement;
isReadyPromise: () => Promise<void>;
}
Get current router informations:
const router = useRouter();
Returns:
useRouter() returns an object with these public properties:
TRoute Current route objectTRoute Previous route objectnumber Current router indexstring Formated base URL(paused:boolean) => void Paused router instance() => void Get paused state of router instance// previousRoute and currentRoute
type TRoute = {
path: string;
component: React.ComponentType<any>;
props?: { [x: string]: any };
parser?: Path;
children?: TRoute[];
matchUrl?: string;
fullUrl?: string;
};
Allow the router to change location.
const [location, setLocation] = useLocation();
// give URL
setLocation("/bar");
// or an object
setLocation({ name: "FooPage", params: { id: "2" } });
Returns:
An array with these properties:
string Get current pathname location(path:string | TOpenRouteParams) => void Open new routetype TOpenRouteParams = {
name: string;
params?: { [x: string]: any };
};
useStack allows to the parent Stack to handle page transitions and refs.
usage:
import React from "react";
import { useStack } from "@cher-ami/router";
const FooPage = forwardRef((props, handleRef) => {
const componentName = "FooPage";
const rootRef = useRef(null);
const playIn = () => new Promise((resolve) => { ... });
const playOut = () => new Promise((resolve) => { ... });
// "handleRef" will get properties via useImperativeHandle
useStack({
componentName,
handleRef,
rootRef,
playIn,
playOut
});
return (
<div className={componentName} ref={rootRef}>
{/* ... */}
</div>
);
});
useStack hook can also receive isReady state from the page component. This
state allows for example to wait for fetching data before page playIn function
is executed.
// ...
const [pageIsReady, setPageIsReady] = useState(false);
useEffect(() => {
// simulate data fetching or whatever for 2 seconds
setTimeout(() => {
setPageIsReady(true);
}, 2000);
}, []);
useStack({
componentName,
handleRef,
rootRef,
playIn,
playOut,
// add the state to useStack
// playIn function wait for isReady to change to true
isReady: pageIsReady,
});
// ...
How does it work? useStack hook registers isReady state and isReadyPromise
in handleRef.
manageTransitions can now use isReadyPromise in its own thread senario.
const customManageTransitions = ({ previousPage, currentPage, unmountPreviousPage }) => {
return new Promise(async (resolve) => {
// ...
// waiting for page "isReady" state to change to continue...
await currentPage?.isReadyPromise?.();
// ...
resolve();
});
};
Demo codesandbox: wait-is-ready
Parameters:
string Name of current componentMutableRefObject<any> Ref handled by parent componentMutableRefObject<any> Ref on root component element() => Promise<any> (optional) Play in transition -
default: new Promise.resolve()() => Promise<any> (optional) Play out transition -
default: new Promise.resolve()boolean (optional) Is ready state - default: trueReturns:
nothing
Returns route counter
const { routeCounter, isFirstRoute, resetCounter } = useRouteCounter();
Parameters:
nothing
Returns:
An object with these properties:
number Current route number - default: 1boolean Check if it's first route - default: true() => void Reset routerCounter & isFirstRoute statesAllow to get the global router history and execute a callback each time history change.
const history = useHistory((e) => {
// do something
});
Parameters:
(event) => void Callback function to execute each time the
history changeReturns:
location[] : global history object. (Routers.history)Get and update langService current language object.
const [lang, setLang] = useLang();
useEffect(() => {
// when current lang change
// it's usefull only if setLang method do not refresh the page.
}, [lang]);
// set new lang with lang object "key" property value only
setLang("en");
// set new lang with the lang object
setLang({ key: "en" });
Returns:
Array of :
TLanguage : current lang object(lang: TLanguage | string, force: boolean) => void : set new lang object (same API than langService.setLang)Manage :lang params from anywhere inside Router scope.
import { LangService } from "@cher-ami/router";
import { Stack } from "./Stack";
const base = "/";
// first lang object is default lang
const languages = [{ key: "en" }, { key: "fr" }, { key: "de" }];
// optionally, default lang can be defined explicitly
// const languages = [{ key: "en" }, { key: "fr", default: true }, { key: "de" }];
// Create LangService instance
const langService = new LangService({
languages,
showDefaultLangInUrl: true,
base,
});
<Router langService={langService} routes={routesList} base={base}>
<App />
</Router>;
Inside the App
function App() {
// get langService instance from router context
const { langService } = useRouter();
return (
<div>
<button onClick={() => langService.setLang({ key: "de" })}>
switch to "de" lang
</button>
<nav>
{/* will return /de */}
<Link to={"/"} />
{/* will return /de/foo */}
<Link to={"/foo"} />
</nav>
<Stack />
</div>
);
}
Methods:
voidInitialize LangService by passing it to "langService" Router props
constructor object properties:
languages: list on language objectsshowDefaultLangInUrl: choose if default language is visible in URL or notbase: set the same than router baseconst langService = new LangService({
languages: [{ key: "en" }, { key: "fr" }],
showDefaultLangInUrl: true,
base: "/",
});
langService instance is available in Router scope from useRouter() hook.
const Page = () => {
const { langService } = useRouter();
// langService.setLang() ...
};
Tlanguage[]Return languages list
const langages = langService.languages;
TLanguageReturn current Language object.
const lang = langService.currentLang;
// { key: "..." }
TLanguageReturn default language object
const defaultLang = langService.defaultLang;
// { key: "..." }
booleanReturn langService init state
const isInit = langService.isInit;
voidSwitch to another available language. This method can be called in nested router component only.
forcePageReload: choose if we reload the full application or using the
internal router stack to change the languagelangService.setLang({ key: "de" });
voidIf URL is /, showDefaultLangInUrl is set to true and default lang is 'en',
it will redirect to /en. This method can be called in nested router component
only.
forcePageReload: choose if we reload the full application or using the
internal router stack to change the languagelangService.redirect();
Paths can be translated by lang in route path property. This option works only if LangService instance is created and passed to the Router component.
{
path: { en: "/foo", fr: "/foo-fr", de: "/foo-de" },
component: FooPage,
}
(args: string | TOpenRouteParams, base?:string, allRoutes?: TRoute[]) => string
Create a formated URL by string, or TOpenRouteParams
(args: string | TOpenRouteParams, history?) => void
Push new route in current history. Stack(s) component(s) will return the appriopriate route.
Routers is a global object who contains all routers informations. Because @cher-ami/router is possibly multi-stack, we need a global object to store shared informations between router instances.
TRoute[]
Final routes array used by the router be
HashHistory | MemoryHistory | BrowserHistory
Selected history mode. all history API is avaible from this one.
LangService
LangService instance given to the first Router component.
number
How many route are resolved from the start of the session. This property is also available from useRouteCounter.
boolean
Is it the first route of the session. This property is also available from useRouteCounter.
FAQs
A fresh react router designed for flexible route transitions
The npm package @cher-ami/router receives a total of 115 weekly downloads. As such, @cher-ami/router popularity was classified as not popular.
We found that @cher-ami/router demonstrated a not healthy version release cadence and project activity because the last version was released a year ago.ย It has 0 open source maintainers collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Research
/Security News
Socket researchers found 10 typosquatted npm packages that auto-run on install, show fake CAPTCHAs, fingerprint by IP, and deploy a credential stealer.

Product
Socket Firewall Enterprise is now available with flexible deployment, configurable policies, and expanded language support.

Security News
Open source dashboard CNAPulse tracks CVE Numbering Authoritiesโ publishing activity, highlighting trends and transparency across the CVE ecosystem.