A simple and safe router for React and TypeScript.

Overview

@swan-io/chicane logo

@swan-io/chicane

mit licence npm version bundlephobia

A simple and safe router for React and TypeScript.

Design principles

  • Typed routes: improving the DX, and making sure all your params are here!
  • Component-friendly: the router nicely fits in your React app.
  • Easy-to-use: naming routes instead of moving around unsafe URLs.
  • Performant: avoids any unnecessary render.

Installation

$ yarn add @swan-io/chicane
# --- or ---
$ npm install --save @swan-io/chicane

Links

Quickstart

import { createRouter } from "@swan-io/chicane";
import { match } from "ts-pattern";

const Router = createRouter({
  Home: "/",
  Users: "/users",
  User: "/users/:userId",
});

const App = () => {
  const route = Router.useRoute(["Home", "Users", "User"]);

  // route object is a discriminated union
  return match(route)
    .with({ name: "Home" }, () => <h1>Home</h1>)
    .with({ name: "Users" }, () => <h1>Users</h1>)
    .with({ name: "User" }, ({ params }) => <h1>User {params.userId}</h1>) // params are strongly typed
    .otherwise(() => <h1>404</h1>);
};

Run the example app

$ git clone [email protected]:swan-io/chicane.git
$ cd chicane/example

$ yarn install && yarn dev
# --- or ---
$ npm install && npm run dev

🙌 Acknowledgements

Comments
  • Expose router-path

    Expose router-path

    Summary

    I wanted quick access to the originally-specified path string, to insert them into the correct place in my react-router routes.

    Test Plan

    Added test proving that .path is passed through exactly as specified in the initial configuration.

    What's required for testing (prerequisites)?

    N/A

    What are the steps to reproduce (after prerequisites)?

    N/A

    Checklist

    • [x] I added the documentation in README.md
    • [ ] I added a sample use of the API in the example project (example/)
    opened by timbuckley 6
  • Add SSR support

    Add SSR support

    Summary

    Add SSR support (providing a URL from a parent component).

    We're currently delaying that to React 18 support, as the useSyncExternalStore shim uses useLayoutEffect, giving us warnings on renderToString.

    opened by bloodyowl 3
  • default to all routes

    default to all routes

    Hi, thanks for your lib, this is more of a question than a serious pull request. I was surprised I had to copy the route names in the useRoute API. Why is this? Defaulting to all routes seems sensible; maybe perf. reasons? Anyways, thanks for your work!

    Summary

    Test Plan

    What's required for testing (prerequisites)?

    What are the steps to reproduce (after prerequisites)?

    Checklist

    • [ ] I added the documentation in README.md
    • [ ] I added a sample use of the API in the example project (example/)
    opened by ssured 3
  • Take the Router Challenge

    Take the Router Challenge

    Why it is needed?

    The Router Challenge aims to be to Routers what TodoMVC was to MV* frameworks. It offers the same Music Catalog application built in React using different Routers. For it to be successful I need the help of Router writers like you. Will you take the Router Challenge and implement the Music Catalog application using Chicane, please?

    Possible implementation

    No response

    Code sample

    No response

    opened by grahammendick 1
  • Replace finite with isArea in matchers

    Replace finite with isArea in matchers

    As part of the Matcher type will be exposed as return type of createGroup in a future version, I want to reduce the lib exposed glossary. Area is already used in our example, it seems fine to me.

    opened by zoontek 0
  • Docs

    Docs

    Summary

    Test Plan

    What's required for testing (prerequisites)?

    What are the steps to reproduce (after prerequisites)?

    Checklist

    • [ ] I added the documentation in README.md
    • [ ] I added a sample use of the API in the example project (example/)
    opened by bloodyowl 0
  • 1.0.0

    1.0.0

    • Remove url from location, add toString
    • Add raw object in location for easier comparaisons
    • Use useSyncExternalStoreWithSelector for useRoute and useRoutes
    • Fix keyboard focus reset on search params / hash only update
    • Multiple methods renaming
    • Add a basic Link component
    opened by zoontek 0
  • Add groupRoutes function

    Add groupRoutes function

    Current usage:

    import { createRouter, groupRoutes } from "react-chicane";
    
    export const { createURL } = createRouter({
      root: "/",
      users: "/users",
      user: "/users/:userId",
    
      ...groupRoutes("project", "/project/:projectId", {
        root: "/",
        members: "/members",
        member: "/members/:memberId",
      }),
    });
    
    createURL("project.root", { projectId: "ID" });
    

    Desired API:

    import { createRouter } from "react-chicane";
    
    export const { createURL } = createRouter(({ group }) => ({
      root: "/",
      users: "/users",
      user: "/users/:userId",
    
      ...group("project", "/project/:projectId", {
        root: "/",
        members: "/members",
        member: "/members/:memberId",
      }),
    }));
    
    createURL("project.root", { projectId: "ID" });
    

    Can't be achieved for now:

    export const createRouter = <
      Routes extends Record<string, string>,
      BasePath extends string = string,
    >(
      routes:
        | Readonly<Routes>
        | ((arg: { group: typeof group }) => Readonly<Routes>), // Readonly<Routes> used as return type will stay Record<string, string>
      options: {
        basePath?: BasePath;
        blockerMessage?: string;
      } = {},
    ) => {
      const finalRoutes = typeof routes === "function" ? routes({ group }) : routes;
    
      // …
    
    opened by zoontek 0
  • Offer active configuration to useLink

    Offer active configuration to useLink

    Summary

    Currently, the algorithm responsive for setting active on useLink is a black box. This might not suit every need (e.g. changing a query-string shouldn't make a section link inactive).

    Due to the optimisation of computing useSubscription to avoid unnecessary re-rerenders, having this comparison function as a prop seems like the most reasonable way to expose this feature.

    opened by bloodyowl 0
  • Use shared history subscription

    Use shared history subscription

    Summary

    Groundwork for further updates:

    • Compare location objects using their stable URL form to prevent extraneous subscriber calls

    • Use a shared history instance called through a useLocation hook for all hooks relying on it

    • Removed the JSON.stringify/JSON.parse bit as location is a more stable object

    • Replace the get location by a getLocation function for DX (breaking change ⚠️)

    • [x] I added the documentation in README.md

    • [x] I added a sample use of the API in the example project (example/)

    opened by bloodyowl 0
  • createLinks without React dependency to generate links for backend

    createLinks without React dependency to generate links for backend

    Why it is needed?

    Hey,

    first of all, thanks for this amazing Router that help us keeps our routes type safe :muscle: We also love the simplicity in it's API and how it leverage existing solution such as ts-pattern.

    For our use-case, we have a monorepo (manage by Nx) and we share some code (Domain) between frontend and backend. The list of app routes are shared and we would also be able to share the generated links functions that is build by the createRouter to be able to use it in backend.

    Here is an example in our user.service.ts

    import { Links } from '@company/shared/domain';
    
        const confirmLink =
          this.environmentContext.appBaseUrl() +
          Links.ConfirmEmail({ token: `${timestamp}-${ident}-${token}` });
    
    

    With this scaffolding, we have type safe links between our backend and frontend :tada:.

    The only issue is that createRouter is coupled with React and create a dependency to React on our backend.

    Possible implementation

    A possible solution could be to publish a createLinks function separately to be imported at it's own path without React dependency.

    We could also publish another package to npm such as @swan-io/chicane-backend or something else.

    For now, as a workaround, we created a file create-links.ts that reuse your code in our domain lib without the React dependency (no push, no useRoute hook) but we have the createURLFunctions that generates all link functions.

    Also could be a good use of a monorepo with 2 libs:

    • core: contains all main functions and utils without React dependency
    • react: contains the binding with React framework and the hook useRoutes

    Publish separately and @swan-io/chicane-react depends on @swan-io/chicane-core.

    Happy to discuss possible solutions if you're interested in this feature :wink.

    Code sample

    Usage could be something like this.

    import { createLinks } from '@swan-io/chicane-core';
    
    // Search params constants
    export const TOKEN_SEARCH_PARAMS = 'token';
    
    export const routes = {
      AppArea: '/*',
      AppRoot: '/',
      // Auth
      Login: '/login?:redirect',
      ForgetPassword: '/forget-password',
      ResetPassword: `/reset-password?:${TOKEN_SEARCH_PARAMS}`,
      Register: '/register?:redirect',
      RedeemInvite: `/redeem-invite?:${TOKEN_SEARCH_PARAMS}`,
    } as const;
    
    export const Links = createLinks(routes);
    
    opened by Nightbr 3
Releases(1.3.2)
  • 1.3.2(Oct 23, 2022)

  • 1.3.1(Oct 18, 2022)

  • 1.3.0(Sep 28, 2022)

    • Add support for search and hash params on wildcard routes (/*).
      This allow some neat tricks, like using a search param from a whole app area:
    export const Router = createRouter({
      Home: "/",
    
      ...createGroup("User", "/users?:impersonatedId", {
        Area: "/*", // UserArea: "/users/*?:impersonatedId" -> You can get impersonatedId value at area root
        Detail: "/:userId", // UserDetail: "/users/:userId?:impersonatedId" -> as it's a group, each route accept impersonatedId param
      }),
    });
    
    Source code(tar.gz)
    Source code(zip)
  • 1.2.5(Aug 25, 2022)

  • 1.2.4(Aug 10, 2022)

  • 1.2.3(Jul 25, 2022)

  • 1.2.2(Jul 9, 2022)

  • 1.2.1(Jul 4, 2022)

  • 1.2.0(May 24, 2022)

  • 1.1.0(May 18, 2022)

    • Improve createGroup concatenation (search and hash params can now be merged too! ✨) - #22
    • Fix wildcard route syntax (it must ends with /*, not only *)
    Source code(tar.gz)
    Source code(zip)
  • 1.0.4(Apr 27, 2022)

  • 1.0.3(Apr 20, 2022)

  • 1.0.2(Apr 6, 2022)

  • 1.0.1(Apr 2, 2022)

  • 1.0.0(Mar 27, 2022)

    This package has been renamed, from react-chicane to @swan-io/chicane.

    ✨ New features

    • We now provide a basic Link component, for fast prototyping.
    • The location object now exposes a unique key and a raw object.

    🔧 Bug fixes

    • Location object / array instances are now stable thought time
    • Fix focus reset on route change (only happen when path change now)

    ⚠️ Breaking changes

    • Router.createURL has been replace by Router.{RouteName} ex: Router.UserDetail({ userId: "123" }); // "/users/123"

    • location.url has been replaced by location.toString()

    • Router.useRoutes hook has been deleted

    • Some parts of the API have been renamed: Router.navigate -> Router.push Router.replace -> Router.replace groupRoutes -> createGroup

    • Some are now are now top-level exports: Router. useLocation -> useLocation

    • Some have been renamed and are now top-level exports: Router.useRouteFocus -> useFocusReset Router.useLink -> useLinkProps Router.useBlocker -> useNavigationBlocker Router.unsafeNavigate -> pushUnsafe Router.unsafeReplace -> replaceUnsafe

    Source code(tar.gz)
    Source code(zip)
  • 0.7.1(Mar 16, 2022)

  • 0.7.0(Mar 5, 2022)

  • 0.6.2(Feb 13, 2022)

  • 0.6.1(Jan 10, 2022)

  • 0.6.0(Jan 7, 2022)

    • New option: Router.useRouter now take an optional parameter, allowing you to specify the matching order.
      Usage: Router.useRoutes([/* … */], { orderBy: "asc" }) (#7 by @zoontek)
    • 🎨 Add a cool project logo (by @bloodyowl)
    Source code(tar.gz)
    Source code(zip)
  • 0.5.0(Jan 7, 2022)

    • ⚠️ Breaking change: The get location function has been replaced by getLocation
    • New method: Router.useRouteFocus, a method used to reset keyboard focus on route change
    • New method: Router. useRoutes, like Router.useRoute but it will returns an array of all matched routes, sorted by ascending specificity
    • Multiple routers now use a shared history instance 🤝
    • location objects are now compared using their stable URL form to prevent extraneous subscriber calls

    PR: https://github.com/zoontek/react-chicane/pull/5 by @bloodyowl

    Source code(tar.gz)
    Source code(zip)
  • 0.4.3(Dec 29, 2021)

  • 0.4.2(Dec 29, 2021)

  • 0.4.1(Nov 17, 2021)

  • 0.4.0(Nov 16, 2021)

  • 0.3.1(Nov 10, 2021)

    • Fix type extraction for routes with path and hash, but no search params (https://github.com/zoontek/react-chicane/commit/d58bd7b93562ddbfc86828185ba15acef2e57c1b)
    Source code(tar.gz)
    Source code(zip)
  • 0.3.0(Nov 4, 2021)

  • 0.2.0(Oct 25, 2021)

    • Merge url in location object (drop the useURL hook in favour of useLocation)
    • Prevent some unnecessary re-renders caused by new params objects instances
    Source code(tar.gz)
    Source code(zip)
  • 0.1.0(Oct 24, 2021)

:tada: Redux First History - Redux history binding support react-router - @reach/router - wouter

redux-first-history Redux First History - Make Redux 100% SINGLE-AND-ONLY source of truth! Redux history binding for react-router @reach/router wouter

Salvatore Ravidà 367 Dec 31, 2022
Redux bindings for React Router – keep your router state inside your Redux store

redux-router This project is experimental. For bindings for React Router 1.x see here In most cases, you don’t need any library to bridge Redux and Re

Andrew Clark 2.3k Dec 7, 2022
React app with TypeScript - React Router Dom

React Project with - React Router Dom My name is Alex Principe. I'm a Full stack developer who shares programming code with the community. This repo c

Alex Principe 2 Sep 20, 2022
A simple middleware-style router for isomorphic JavaScript web apps

Universal Router A simple middleware-style router that can be used in both client-side and server-side applications. Visit Quickstart Guide (slides) |

Kriasoft 1.6k Jan 6, 2023
Render isomorphic React + React Router apps and components in Node

Render isomorphic React + React Router apps and components in Node

Sequence Media 3 Nov 1, 2022
named routes for react-router and your react application

react-router-namesake Example import { Switch } from "react-router"; import { BrowserRouter, Route, Link } from "react-router-dom"; import { Router as

Joe Hsu 6 Aug 12, 2021
You can found the concept of react hooks, local storage, conditional rendering and react-router-dom.

Getting Started with Create React App This project was bootstrapped with Create React App. Available Scripts In the project directory, you can run: np

Tikaram Acharya 2 Jun 1, 2022
a more intuitive way of defining private, public and common routes for react applications using react-router-dom v6

auth-react-router is a wrapper over react-router-dom v6 that provides a simple API for configuring public, private and common routes (React suspense r

Pasecinic Nichita 12 Dec 3, 2022
A custom React router that leverages the Web Animations API and CSS animations.

A custom React router that leverages the Web Animations API and CSS animations.

Nate Adams 28 Jan 18, 2021
Route Sphere - Sync query parameters with a MobX store and React Router

Route Sphere - Sync query parameters with a MobX store and React Router

VocaDB Devgroup 1 May 21, 2022
VSCode extension to generate a router based on file based routing and nested layouts

vscode-router-generator VSCode Extension to generate a router based on a file structure and returning the correct nested layouts. There can be optiona

Rody Davis 8 Sep 29, 2022
Frontend of agro rent app built with React, Axios, React-router-dom v6 & Bootstrap

Getting Started with Create React App This project was bootstrapped with Create React App. Available Scripts In the project directory, you can run: ya

Anuj Sharma 1 Dec 8, 2021
Automatic breadcrumbs for React-Router

React Breadcrumbs React component use to generate a breadcrumb trail (compatible with React Router). Installation npm install --save react-breadcrumbs

Sven Anders Robbestad 409 Dec 9, 2022
Declarative router component for React.

React Router Component Version Compatibility >= 0.39.0 React v15,16 >= 0.32.0 React v15 >= 0.27.0 React 0.14 0.24 - 0.26.0 React 0.13 0.23 - 0.26.0 Re

Samuel Reed 871 Nov 29, 2022
React Router scroll management

react-router-scroll React Router scroll management. react-router-scroll is a React Router middleware that adds scroll management using scroll-behavior

Jimmy Jia 846 Dec 18, 2022
🔼 UI-Router for React

UI-Router provides extremely flexible, state based routing to the React ecosystem.

UI-Router 444 Dec 22, 2022
Easy Router for Effector with React bindings

effector-easy-router A declarative router for effector and react. It is inspired by react-router-dom and effector gates. Routes are independent from e

Kirill Kubryakov 10 Oct 7, 2022
React router that supports rendering of multiple independent (auxiliary) routes.

aux-router React router that supports rendering of multiple independent (auxiliary) routes. Install npm install --save aux-router Documentation AuxRou

Kamil Bugno 20 Oct 4, 2021
A small router library for React focusing on the Developer Experience

Crossroad A routing library for React with a familiar interface. It has some differences with React Router so you write cleaner code: The links are pl

Francisco Presencia 17 Dec 10, 2022