ルートファイルの命名

「routes」プラグインオプションでルートを構成することもできますが、ほとんどのルートは、このファイルシステム規則を使用して作成されます。ファイルを追加すれば、ルートが作成されます。

.js.jsx.ts.tsxのいずれかのファイル拡張子を使用できます。重複を避けるために、例では.tsxを使用します。

ディルム・サンジャヤは、素晴らしい可視化を作成しました。これは、ファイルシステム内のルートがアプリのURLにどのようにマッピングされるかを示しており、これらの規則を理解するのに役立ちます。

免責事項

Remixの規則に深く踏み込む前に、ファイルベースのルーティングは非常に主観的なアイデアであることを指摘しておきたいと思います。フラットなルートのアイデアを気に入っている人もいれば、嫌いでルートをフォルダ内にネストしたい人もいます。ファイルベースのルーティングを単純に嫌いで、JSONでルートを構成したい人もいます。React Router SPAで行っていたように、JSXでルートを構成したい人もいます。

要点は、私たちはこれを知っており、最初からRemixは、routes/ignoredRouteFilesを介してオプトアウトし、ルートを手動で構成するためのファーストクラスの方法を提供してきました。しかし、人々が迅速かつ簡単に開始できるように、_何らかの_デフォルトが必要です。そして、私たちは、以下のフラットなルート規則ドキュメントは、小規模から中規模のアプリにうまくスケールする、かなり良いデフォルトだと考えています。

数百または数千ものルートを持つ大規模なアプリケーションでは、どのような規則を使用しても、_常に_多少は混沌としています。そして、routes構成を使用すれば、アプリケーション/チームにとって最適な規則を_正確に_構築できます。Remixに、すべての人が満足するデフォルト規則を持たせるのは、事実上不可能です。むしろ、私たちは、かなりわかりやすいデフォルトを提供し、コミュニティがさまざまな規則を構築できるようにしたいと考えています。そこから、自由に選択できます。

そこで、Remixのデフォルト規則の詳細について説明する前に、デフォルトが気に入らない場合は、確認できるコミュニティの代替規則をいくつか紹介します。

  • remix-flat-routes - Remixのデフォルトは、基本的にこのパッケージの簡略版です。作者は、このパッケージの反復と進化を続けているため、一般的に「フラットルート」のアイデアは好きですが、もう少しパワーが欲しい場合は(ファイルとフォルダのハイブリッドアプローチを含む)、このパッケージをぜひ確認してください。
  • remix-custom-routes - さらにカスタマイズしたい場合は、このパッケージを使用すると、どのタイプのファイルをルートとして扱うかを定義できます。これにより、単純なフラット/ネストされた概念を超えて、たとえば「.route.tsxという拡張子のファイルはすべてルート」などを行うことができます。
  • remix-json-routes - 構成ファイルでルートを指定したいだけなら、これが最適です。Remixにルートを含むJSONオブジェクトを提供し、フラット/ネストされた概念を完全にスキップします。JSXオプションも含まれています。

ルートルート

app/
├── routes/
└── root.tsx

app/root.tsx内のファイルは、ルートレイアウトまたは「ルートルート」です(これらの単語を同じように発音する人は本当に申し訳ありません!)。これは、他のすべてのルートと同じように機能するため、loaderactionなどをエクスポートできます。

ルートルートは通常、このようなものです。これは、アプリ全体のルートレイアウトとして機能し、他のすべてのルートは<Outlet />内でレンダリングされます。

import {
  Links,
  Meta,
  Outlet,
  Scripts,
  ScrollRestoration,
} from "@remix-run/react";
 
export default function Root() {
  return (
    <html lang="en">
      <head>
        <Links />
        <Meta />
      </head>
      <body>
        <Outlet />
        <ScrollRestoration />
        <Scripts />
      </body>
    </html>
  );
}

基本的なルート

app/routesディレクトリ内のJavaScriptまたはTypeScriptファイルはすべて、アプリケーション内のルートになります。ファイル名は、ルートのURLパス名にマッピングされます。ただし、_index.tsxは、ルートルートインデックスルートです。

app/
├── routes/
│   ├── _index.tsx
│   └── about.tsx
└── root.tsx
URLマッチするルート
/app/routes/_index.tsx
/aboutapp/routes/about.tsx

これらのルートは、ネストされたルーティングのため、app/root.tsxのアウトレットにレンダリングされます。

ドットデリミタ

ルートファイル名に.を追加すると、URLに/が作成されます。

 app/
├── routes/
│   ├── _index.tsx
│   ├── about.tsx
│   ├── concerts.trending.tsx
│   ├── concerts.salt-lake-city.tsx
│   └── concerts.san-diego.tsx
└── root.tsx
URLマッチするルート
/app/routes/_index.tsx
/aboutapp/routes/about.tsx
/concerts/trendingapp/routes/concerts.trending.tsx
/concerts/salt-lake-cityapp/routes/concerts.salt-lake-city.tsx
/concerts/san-diegoapp/routes/concerts.san-diego.tsx

ドットデリミタは、ネストも作成します。詳細については、ネストセクションを参照してください。

動的セグメント

通常、URLは静的ではなく、データ駆動型です。動的セグメントを使用すると、URLのセグメントを一致させ、その値をコードで使用できます。$プレフィックスで作成します。

 app/
├── routes/
│   ├── _index.tsx
│   ├── about.tsx
│   ├── concerts.$city.tsx
│   └── concerts.trending.tsx
└── root.tsx
URLマッチするルート
/app/routes/_index.tsx
/aboutapp/routes/about.tsx
/concerts/trendingapp/routes/concerts.trending.tsx
/concerts/salt-lake-cityapp/routes/concerts.$city.tsx
/concerts/san-diegoapp/routes/concerts.$city.tsx

Remixは、URLから値を解析し、さまざまなAPIに渡します。これらの値を「URLパラメータ」と呼びます。URLパラメータにアクセスする最も便利な場所は、loaderactionです。

export async function loader({
  params,
}: LoaderFunctionArgs) {
  return fakeDb.getAllConcertsForCity(params.city);
}

paramsオブジェクトのプロパティ名は、ファイル名に直接マッピングされます。$city.tsxparams.cityになります。

ルートには、concerts.$city.$dateのように、複数の動的セグメントを含めることができます。どちらも、paramsオブジェクトで名前でアクセスされます。

export async function loader({
  params,
}: LoaderFunctionArgs) {
  return fake.db.getConcerts({
    date: params.date,
    city: params.city,
  });
}

詳細については、ルーティングガイドを参照してください。

ネストされたルート

ネストされたルーティングとは、URLのセグメントとコンポーネントの階層およびデータを結びつける一般的な考え方です。詳細については、ルーティングガイドを参照してください。

ドットデリミタを使用して、ネストされたルートを作成します。.前のファイル名が別のルートファイル名と一致する場合、自動的に一致する親ルートの子ルートになります。次のルートを考えてみてください。

 app/
├── routes/
│   ├── _index.tsx
│   ├── about.tsx
│   ├── concerts._index.tsx
│   ├── concerts.$city.tsx
│   ├── concerts.trending.tsx
│   └── concerts.tsx
└── root.tsx

app/routes/concerts.で始まるすべてのルートは、app/routes/concerts.tsxの子ルートになり、親ルートのoutlet_component内でレンダリングされます。

URLマッチするルートレイアウト
/app/routes/_index.tsxapp/root.tsx
/aboutapp/routes/about.tsxapp/root.tsx
/concertsapp/routes/concerts._index.tsxapp/routes/concerts.tsx
/concerts/trendingapp/routes/concerts.trending.tsxapp/routes/concerts.tsx
/concerts/salt-lake-cityapp/routes/concerts.$city.tsxapp/routes/concerts.tsx

通常、ネストされたルートを追加するときは、インデックスルートを追加して、ユーザーが親URLに直接アクセスした場合に、親のアウトレット内に何かがレンダリングされるようにします。

たとえば、URLが/concerts/salt-lake-cityの場合、UI階層は次のようになります。

<Root>
  <Concerts>
    <City />
  </Concerts>
</Root>

レイアウトのネストなしにネストされたURL

これらをパスのないルートと呼びます

URLをネストしたい場合でも、自動的なレイアウトのネストは不要な場合があります。親セグメントに下線を追加すると、ネストをオプトアウトできます。

 app/
├── routes/
│   ├── _index.tsx
│   ├── about.tsx
│   ├── concerts.$city.tsx
│   ├── concerts.trending.tsx
│   ├── concerts.tsx
│   └── concerts_.mine.tsx
└── root.tsx
URLマッチするルートレイアウト
/app/routes/_index.tsxapp/root.tsx
/aboutapp/routes/about.tsxapp/root.tsx
/concerts/mineapp/routes/concerts_.mine.tsxapp/root.tsx
/concerts/trendingapp/routes/concerts.trending.tsxapp/routes/concerts.tsx
/concerts/salt-lake-cityapp/routes/concerts.$city.tsxapp/routes/concerts.tsx

/concerts/mineは、app/routes/concerts.tsxではなく、app/root.tsxにネストされていないことに注意してください。trailing_の下線は、パスセグメントを作成しますが、レイアウトのネストは作成しません。

trailing_の下線を、親の署名の下部の長い部分と考えてください。あなたを遺言から除外して、それに続くセグメントをレイアウトのネストから削除しています。

ネストされたレイアウトとネストされていないURL

これらをパスのないルートと呼びます

URLにパスセグメントを追加せずに、ルートグループでレイアウトを共有したい場合があります。一般的な例としては、公開ページやログイン済みのアプリエクスペリエンスとは異なるヘッダー/フッターを持つ一連の認証ルートがあります。_leadingの下線を使用すると、これを実現できます。

 app/
├── routes/
│   ├── _auth.login.tsx
│   ├── _auth.register.tsx
│   ├── _auth.tsx
│   ├── _index.tsx
│   ├── concerts.$city.tsx
│   └── concerts.tsx
└── root.tsx
URLマッチするルートレイアウト
/app/routes/_index.tsxapp/root.tsx
/loginapp/routes/_auth.login.tsxapp/routes/_auth.tsx
/registerapp/routes/_auth.register.tsxapp/routes/_auth.tsx
/concertsapp/routes/concerts.tsxapp/root.tsx
/concerts/salt-lake-cityapp/routes/concerts.$city.tsxapp/routes/concerts.tsx

_leadingの下線を、ファイル名にかぶせる毛布と考えてください。ファイル名をURLから隠しています。

オプションのセグメント

ルートセグメントを括弧で囲むと、そのセグメントがオプションになります。

 app/
├── routes/
│   ├── ($lang)._index.tsx
│   ├── ($lang).$productId.tsx
│   └── ($lang).categories.tsx
└── root.tsx
URLマッチするルート
/app/routes/($lang)._index.tsx
/categoriesapp/routes/($lang).categories.tsx
/en/categoriesapp/routes/($lang).categories.tsx
/fr/categoriesapp/routes/($lang).categories.tsx
/american-flag-speedoapp/routes/($lang)._index.tsx
/en/american-flag-speedoapp/routes/($lang).$productId.tsx
/fr/american-flag-speedoapp/routes/($lang).$productId.tsx

/american-flag-speedo($lang).$productId.tsxではなく、($lang)._index.tsxルートと一致するのはなぜでしょうか?これは、オプションの動的パラメータセグメントの後に別の動的パラメータがある場合、Remixは、/american-flag-speedoのような単一セグメントのURLが/:lang /:productIdと一致するかどうかを確実に判断できないためです。オプションのセグメントは熱心に一致するため、/:langと一致します。このような設定がある場合は、($lang)._index.tsxのローダーでparams.langを確認し、params.langが有効な言語コードでない場合は、現在の/デフォルトの言語の/:lang/american-flag-speedoにリダイレクトすることをお勧めします。

スプラットルート

動的セグメントは単一のパスセグメント(URL内の2つの/の間にあるもの)と一致しますが、スプラットルートはスラッシュを含むURLの残りの部分と一致します。

 app/
├── routes/
│   ├── _index.tsx
│   ├── $.tsx
│   ├── about.tsx
│   └── files.$.tsx
└── root.tsx
URLマッチするルート
/app/routes/_index.tsx
/aboutapp/routes/about.tsx
/beef/and/cheeseapp/routes/$.tsx
/filesapp/routes/files.$.tsx
/files/talks/remix-conf_old.pdfapp/routes/files.$.tsx
/files/talks/remix-conf_final.pdfapp/routes/files.$.tsx
/files/talks/remix-conf-FINAL-MAY_2022.pdfapp/routes/files.$.tsx

動的ルートパラメータと同様に、スプラットルートのparamsで、"*"キーを使用して、一致したパスの値にアクセスできます。

app/routes/files.$.tsx
export async function loader({
  params,
}: LoaderFunctionArgs) {
  const filePath = params["*"];
  return fake.getFileInfo(filePath);
}

特殊文字のエスケープ

これらのルート規則で使用される特殊文字のいずれかを、実際にURLの一部として使用したい場合は、[]文字を使用して規則をエスケープできます。

ファイル名URL
app/routes/sitemap[.]xml.tsx/sitemap.xml
app/routes/[sitemap.xml].tsx/sitemap.xml
app/routes/weird-url.[_index].tsx/weird-url/_index
app/routes/dolla-bills-[$].tsx/dolla-bills-$
app/routes/[[so-weird]].tsx/[so-weird]

整理のためのフォルダ

ルートは、フォルダとして作成することもできます。その中には、ルートモジュールを定義するroute.tsxファイルが入っています。フォルダ内の他のファイルはルートになりません。これにより、コードを、別のフォルダで機能名を繰り返すのではなく、コードを使用するルートに近づけて整理できます。

フォルダ内のファイルは、ルートパスには意味を持ちません。ルートパスは、フォルダ名によって完全に定義されます

次のルートを考えてみてください。

 app/
├── routes/
│   ├── _landing._index.tsx
│   ├── _landing.about.tsx
│   ├── _landing.tsx
│   ├── app._index.tsx
│   ├── app.projects.tsx
│   ├── app.tsx
│   └── app_.projects.$id.roadmap.tsx
└── root.tsx

一部またはすべてのルートを、独自のrouteモジュールを内部に含むフォルダにすることができます。

app/
├── routes/
│   ├── _landing._index/
│   │   ├── route.tsx
│   │   └── scroll-experience.tsx
│   ├── _landing.about/
│   │   ├── employee-profile-card.tsx
│   │   ├── get-employee-data.server.ts
│   │   ├── route.tsx
│   │   └── team-photo.jpg
│   ├── _landing/
│   │   ├── footer.tsx
│   │   ├── header.tsx
│   │   └── route.tsx
│   ├── app._index/
│   │   ├── route.tsx
│   │   └── stats.tsx
│   ├── app.projects/
│   │   ├── get-projects.server.ts
│   │   ├── project-buttons.tsx
│   │   ├── project-card.tsx
│   │   └── route.tsx
│   ├── app/
│   │   ├── footer.tsx
│   │   ├── primary-nav.tsx
│   │   └── route.tsx
│   ├── app_.projects.$id.roadmap/
│   │   ├── chart.tsx
│   │   ├── route.tsx
│   │   └── update-timeline.server.ts
│   └── contact-us.tsx
└── root.tsx

ルートモジュールをフォルダに変換すると、ルートモジュールはfolder/route.tsxになり、フォルダ内の他のモジュールはルートになりません。たとえば、次のようになります。

# これらは同じルートです。
app/routes/app.tsx
app/routes/app/route.tsx

# これらも同様です
app/routes/app._index.tsx
app/routes/app._index/route.tsx

スケール

一般的に、スケールするためには、すべてのルートをフォルダにし、そのルートでしか使用されないモジュールをフォルダ内に配置し、共有モジュールをルートフォルダの外部の別の場所に配置することをお勧めします。これには、次の利点があります。

  • 共有モジュールを簡単に特定できるため、変更する際は注意深く行うことができます
  • 特定のルートのモジュールを簡単に整理してリファクタリングできるため、「ファイル整理の疲労」が解消され、アプリの他の部分に不要なものを散乱させることがなくなります