
Research
2025 Report: Destructive Malware in Open Source Packages
Destructive malware is rising across open source registries, using delays and kill switches to wipe code, break builds, and disrupt CI/CD.
react-chicane
Advanced tools
A simple and safe router for React and TypeScript.
yarn add react-chicane
git clone git@github.com:zoontek/react-chicane.git
cd react-chicane/example
yarn install && yarn dev
This library exports a main function: createRouter. The goal behind this is to enforce listing all your project routes using fancy names in a file and use the strongly typed methods returned.
import { createRouter } from "react-chicane";
const { useRoute } = createRouter({
root: "/",
users: "/users",
user: "/users/:userId",
});
const App = () => {
const route = useRoute(["root", "users", "user"]);
if (!route) {
return <h1>404</h1>;
}
// route object is a discriminated union
switch (route.name) {
case "root":
return <h1>Homepage</h1>;
case "users":
return <h1>Users</h1>;
case "user":
// params are strongly typed
return <h1>User {route.params.userId}</h1>;
}
};
react-chicane doesn't bother about what's inside your path, your search params or your hash. It only exposes an object, params.
stringstringstring[]import { createRouter } from "react-chicane";
import { match } from "ts-pattern";
export const { useRoute } = createRouter({
groups: "/groups",
group: "/groups/:groupId?:foo&:bar[]#:baz",
users: "/groups/:groupId/users",
user: "/groups/:groupId/users/:userId",
// it also supports wildcard routes!
usersArea: "/groups/:groupId/users/*",
});
const App = () => {
const route = useRoute(["groups", "group", "users", "user"]);
match(route)
.with({ name: "groups" }, ({ params }) => console.log(params)) // {}
.with({ name: "group" }, ({ params }) => console.log(params)) // { groupId: string, foo?: string, bar?: string[], baz?: string }
.with({ name: "users" }, ({ params }) => console.log(params)) // { groupId: string }
.with({ name: "user" }, ({ params }) => console.log(params)) // { groupId: string, userId: string }
.otherwise(() => <h1>404</h1>);
// …
};
Because it's nice to create safe internal URLs, createRouter also returns createURL.
import { createRouter } from "react-chicane";
const { createURL } = createRouter({
root: "/",
users: "/users",
user: "/users/:userId",
});
createURL("root"); // -> "/"
createURL("users"); // -> "/users"
createURL("user", { userId: "zoontek" }); // -> "/users/zoontek"
Create a router instance for your whole application.
import { createRouter } from "react-chicane";
const Router = createRouter(
{
root: "/",
users: "/users",
user: "/users/:userId",
},
{
basePath: "/setup/basePath/here", // Will be prepend to all your paths (optional)
blockerMessage: "Are you sure you want to leave this page?", // A default navigation blocker message (optional)
},
);
Router instance.type Location = {
url: string;
path: string[];
search: Record<string, string | string[]>;
hash?: string;
};
Router.getLocation(); // Location
Navigate to a given route.
Router.navigate("root");
Router.navigate("users");
Router.navigate("user", { userId: "zoontek" });
Same as navigate, but will replace the current route in the browser history.
Router.replace("root");
Router.replace("users");
Router.replace("user", { userId: "zoontek" });
Go back in browser history.
Router.goBack();
Go forward in browser history.
Router.goForward();
Safely create internal URLs.
Router.createURL("root"); // -> "/"
Router.createURL("users"); // -> "/users"
Router.createURL("user", { userId: "zoontek" }); // -> "/users/zoontek"
Listen and match a bunch of your routes. Awesome with pattern matching.
import { match } from "ts-pattern";
const App = () => {
// The order isn't important, paths are ranked using https://reach.tech/router/ranking
const route = Router.useRoute(["root", "users", "user"]);
return match(route)
.with({ name: "root" }, () => <h1>root</h1>)
.with({ name: "users" }, () => <h1>users</h1>)
.with({ name: "user" }, ({ params: { userId } }) => <h1>user</h1>)
.otherwise(() => <h1>404</h1>);
};
Registers a component as a route container, so that the element receives focus on route change. When using nested routes, the deepest route container is focused.
const App = () => {
const route = Router.useRoute(["root", "users", "user"]);
const containerRef = React.useRef(null);
Router.useRouteFocus({ containerRef, route });
return <div ref={containerRef}>{/* match your route here */}</div>;
};
Listen and match a bunch of your routes. Returns an array of routes, sorted by descending specificity. Useful for route hierarchical representation (e.g. a breadcrumb component).
import { match } from "ts-pattern";
const Breadcrumbs = () => {
const routes = Router.useRoutes(["root", "users", "user"], {
orderBy: "asc", // accepts "asc" or "desc" order (default is "desc")
});
return routes.map((route) =>
match(route)
.with({ name: "root" }, () => "Home")
.with({ name: "users" }, () => "Users")
.with({ name: "user" }, ({ params: { userId } }) => userId)
.otherwise(() => null),
);
};
As this library doesn't provide a single component, we expose this hook to create your own customized Link.
const Link = ({
children,
to,
replace,
target,
}: {
children?: React.ReactNode;
to: string;
replace?: boolean;
target?: React.HTMLAttributeAnchorTarget;
}) => {
const { active, onClick } = useLink({ href: to, replace, target });
return (
<a
href={to}
onClick={onClick}
target={target}
style={{ fontWeight: active ? 700 : 400 }}
>
{children}
</a>
);
};
// usage
<Link to={Router.createURL("user", { userId: "zoontek" })}>Profile</Link>;
Listen and react on Router.location changes.
const App = () => {
const location: Location = Router.useLocation();
React.useEffect(() => {
console.log("location changed", location);
}, [location]);
// …
};
Block the navigation and ask user for confirmation. Useful to avoid loosing a form state. It accepts a second paramater if you want to override the default blockerMessage.
const App = () => {
const { formStatus } = useForm(/* … */);
Router.useBlocker(
formStatus === "editing",
"Are you sure you want to stop editing this profile?",
);
// …
};
Subscribe to location changes.
const unsubscribe = Router.subscribe((location: Location) => {
// …
});
Two methods similar to Router.navigate and Router.replace but which accept a string as unique argument. Useful for escape hatches.
A quick example with a Redirect component:
const Redirect = ({ to }: { to: string }) => {
const { url } = Router.useLocation();
React.useLayoutEffect(() => {
if (to !== url) {
Router.unsafeReplace(to);
}
}, []);
return null;
};
// usage
<Redirect to={Router.createURL("root")} />;
Reduce routes declaration repetitions by subpath grouping.
import { createRouter, groupRoutes } from "react-chicane";
const Router = createRouter({
root: "/",
user: "/:userName",
...groupRoutes("repository", "/:repositoryName", {
root: "/",
issues: "/issues",
pulls: "/pulls",
actions: "/actions",
// Can be nested indefinitely
...groupRoutes("settings", "/settings", {
root: "/",
collaborators: "/access",
branches: "/branches",
}),
}),
});
Router.createURL("user", { userName: "zoontek" });
Router.createURL("repository.actions", { repositoryName: "valienv" });
Router.createURL("repository.settings.branches", { repositoryName: "valienv" });
Encode and decode url search parameters.
import { decodeSearch, encodeSearch } from "react-chicane";
encodeSearch({ invitation: "542022247745", users: ["frank", "chris"] });
// -> "?invitation=542022247745&users=frank&users=chris"
decodeSearch("?invitation=542022247745&users=frank&users=chris");
// -> { invitation: "542022247745", users: ["frank", "chris"] }
history and the Link creation code.FAQs
A simple and safe router for React and TypeScript
We found that react-chicane demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 1 open source maintainer 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
Destructive malware is rising across open source registries, using delays and kill switches to wipe code, break builds, and disrupt CI/CD.

Security News
Socket CTO Ahmad Nassri shares practical AI coding techniques, tools, and team workflows, plus what still feels noisy and why shipping remains human-led.

Research
/Security News
A five-month operation turned 27 npm packages into durable hosting for browser-run lures that mimic document-sharing portals and Microsoft sign-in, targeting 25 organizations across manufacturing, industrial automation, plastics, and healthcare for credential theft.