
Security News
npm Adopts OIDC for Trusted Publishing in CI/CD Workflows
npm now supports Trusted Publishing with OIDC, enabling secure package publishing directly from CI/CD workflows without relying on long-lived tokens.
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
.
string
string
string[]
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
The npm package react-chicane receives a total of 0 weekly downloads. As such, react-chicane popularity was classified as not popular.
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.
Security News
npm now supports Trusted Publishing with OIDC, enabling secure package publishing directly from CI/CD workflows without relying on long-lived tokens.
Research
/Security News
A RubyGems malware campaign used 60 malicious packages posing as automation tools to steal credentials from social media and marketing tool users.
Security News
The CNA Scorecard ranks CVE issuers by data completeness, revealing major gaps in patch info and software identifiers across thousands of vulnerabilities.