React Router で Remix ライクなファイルベースルーティング

2024/11追記

React Router v7 を使いましょう

追記終わり


React Router と Vite の環境で、Remix ライクなファイルベースルーティングシステムを実装してみた。

https://github.com/isoden/react-router-file-based-routing

React Router でファイルベースルーティングを実現する仕組み自体は新しいものではない。有名なところだと https://github.com/oedotme/generouted がある。

既存のものと違うのは、 Remix の Route File Naming をサポートしている点。

README にも書いたが、以下のディレクトリ構造があると、次の routes オブジェクトが生成される。

src/routes
├── 404.tsx
├── _auth.login.tsx
├── _auth.register.tsx
├── _auth.tsx
├── _index.tsx
├── _root.tsx
├── app
│   └── route.tsx
├── app._index
│   └── route.tsx
├── app.projects
│   └── route.tsx
├── app_.projects.$id.roadmap
│   └── route.tsx
├── concerts.$city.tsx
├── concerts._index.tsx
├── concerts.trending.tsx
├── concerts.tsx
├── concerts_.mine.tsx
├── files.$.tsx
├── optional.($lang).$productId.tsx
└── optional.($lang).categories.tsx
const routes: RouteObject[] = [
  {
    /* Component: import('src/routes/_root') */
    children: [
      {
        index: true,
        /* lazy: import('src/routes/_index') */
      },
      {
        /* lazy: import('src/routes/_auth') */
        children: [
          {
            path: 'login',
            /* lazy: import('src/routes/_auth.login') */
          },
          {
            path: 'register',
            /* lazy: import('src/routes/_auth.register') */
          },
        ],
      },
      {
        path: 'app',
        /* lazy: import('src/routes/app/route') */
        children: [
          {
            index: true,
            /* lazy: import('src/routes/app._index/route') */
          },
          {
            path: 'projects',
            /* lazy: import('src/routes/app.projects/route') */
          },
        ],
      },
      {
        path: 'app/projects/:id/roadmap',
        /* lazy: import('src/routes/app_.projects.$id.roadmap/route') */
      },
      {
        path: 'concerts',
        /* lazy: import('src/routes/concerts') */
        children: [
          {
            index: true,
            /* lazy: import('src/routes/concerts._index') */
          },
          {
            path: 'trending',
            /* lazy: import('src/routes/concerts.trending') */
          },
          {
            path: ':city',
            /* lazy: import('src/routes/concerts.$city') */
          },
        ],
      },
      {
        path: 'concerts/mine',
        /* lazy: import('src/routes/concerts_.mine') */
      },
      {
        path: 'files/*',
        /* lazy: import('src/routes/files.$') */
      },
      {
        path: 'optional',
        children: [
          {
            path: 'lang?',
            children: [
              {
                path: 'categories',
                /* lazy: import('src/routes/optional.($lang).categories') */
              },
              {
                path: ':productId',
                /* lazy: import('src/routes/optional.($lang).$productId') */
              },
            ],
          },
        ],
      },
      {
        path: '*',
        /* Component: import('src/routes/404') */
      },
    ],
  },
]

動作確認環境 (StackBlitz) はこちら。

なぜこれを書いたか。Create React App 時代から開発・保守を続けているプロジェクトがあって、そこにファイルベースルーティングを導入したかったからだ(バンドラー自体は Vite に移行済み)。

今から新規に SPA を作るなら Remix の SPA mode も選択肢に上がると思うが、そう簡単にフレームワークを導入(または移行)できないプロジェクトも多いだろう。

WIP

No comments yet