T-CREATOR

Remix ルーティング早見表:ネスト・可変パラメータ・モーダルルート対応一覧

Remix ルーティング早見表:ネスト・可変パラメータ・モーダルルート対応一覧

Remix のファイルシステムベースルーティングは、シンプルで直感的な設計が魅力ですが、ネスト構造や可変パラメータ、モーダル表示など、少し複雑なパターンになると「この場合はどうファイルを配置すればいいの?」と迷うことも多いですよね。

この記事では、Remix のルーティングパターンを体系的に整理し、実際のファイル配置とそれに対応する URL パスを一覧表でまとめました。基本パターンから、ネストレイアウト、動的パラメータ、パスなしレイアウト、モーダルルートまで、網羅的に解説します。この早見表を参考にすれば、どんなルーティング要件にも素早く対応できるようになるでしょう。

ルーティングパターン早見表

全パターン一覧

#カテゴリファイルパスURL パスパラメータ説明
1基本app​/​routes​/​_index.tsx​/​-トップページ
2基本app​/​routes​/​about.tsx​/​about-単一の静的ページ
3基本app​/​routes​/​contact.tsx​/​contact-単一の静的ページ
4基本app​/​routes​/​blog._index.tsx​/​blog-​/​blog のインデックスページ
5基本app​/​routes​/​blog.posts.tsx​/​blog​/​posts-​/​blog​/​posts ページ
6動的app​/​routes​/​users.$userId.tsx​/​users​/​:userIduserIdユーザー詳細ページ
7動的app​/​routes​/​posts.$postId.tsx​/​posts​/​:postIdpostId投稿詳細ページ
8動的app​/​routes​/​products.$productId.tsx​/​products​/​:productIdproductId商品詳細ページ
9動的app​/​routes​/​blog.$slug.tsx​/​blog​/​:slugslugブログ記事ページ
10動的app​/​routes​/​users.$userId.edit.tsx​/​users​/​:userId​/​edituserIdユーザー編集ページ
11ネストapp​/​routes​/​blog.tsx​/​blog-親レイアウト(Outlet で子を表示)
12ネストapp​/​routes​/​blog._index.tsx​/​blog-​/​blog のインデックス
13ネストapp​/​routes​/​blog.posts.tsx​/​blog​/​posts-ブログ投稿一覧
14ネストapp​/​routes​/​blog.posts.$postId.tsx​/​blog​/​posts​/​:postIdpostIdブログ投稿詳細
15ネストapp​/​routes​/​blog.posts.$postId.edit.tsx​/​blog​/​posts​/​:postId​/​editpostIdブログ投稿編集
16パスなしapp​/​routes​/​_app.tsx--レイアウトのみ(URL に現れない)
17パスなしapp​/​routes​/​_app.dashboard.tsx​/​dashboard-_app レイアウトを継承
18パスなしapp​/​routes​/​_app.settings.tsx​/​settings-_app レイアウトを継承
19パスなしapp​/​routes​/​_auth.tsx--認証レイアウト
20パスなしapp​/​routes​/​_auth.login.tsx​/​login-ログインページ(_auth レイアウトを継承)
21パスなしapp​/​routes​/​_auth.register.tsx​/​register-登録ページ(_auth レイアウトを継承)
22Splatapp​/​routes​/​$.tsx​/​**すべてのパスをキャッチ
23Splatapp​/​routes​/​docs.$.tsx​/​docs​/​**​/​docs 配下の全パスをキャッチ
24Splatapp​/​routes​/​files.$path.$.tsx​/​files​/​:path​/​*path, *複数セグメントをキャッチ
25モーダルapp​/​routes​/​photos.tsx​/​photos-写真一覧ページ
26モーダルapp​/​routes​/​photos.$photoId.modal.tsx​/​photos​/​:photoIdphotoIdモーダルで写真詳細を表示
27モーダルapp​/​routes​/​products.tsx​/​products-商品一覧ページ
28モーダルapp​/​routes​/​products.$productId.modal.tsx​/​products​/​:productIdproductIdモーダルで商品詳細を表示
29複合app​/​routes​/​users.$userId.posts.$postId.tsx​/​users​/​:userId​/​posts​/​:postIduserId, postIdユーザーの投稿詳細
30複合app​/​routes​/​orgs.$orgId.projects.$projectId.tsx​/​orgs​/​:orgId​/​projects​/​:projectIdorgId, projectId組織のプロジェクト詳細
31複合app​/​routes​/​stores.$storeId.products.$productId.tsx​/​stores​/​:storeId​/​products​/​:productIdstoreId, productId店舗の商品詳細

記号の役割早見表

記号役割URL パス
.パスの区切りblog.posts.tsx​/​blog​/​posts
$動的パラメータ$userId.tsx​/​:userId
_ (プレフィックス)パスなしレイアウト_app.tsxURL に影響なし
_indexインデックスルートblog._index.tsx​/​blog
()グループ化(整理用)(auth).login.tsx​/​login
$.tsxSplat ルート$.tsx​/​* (全パス)

背景

ファイルシステムベースルーティングとは

Remix は Next.js のページルーターと同様に、app​/​routes ディレクトリ内のファイル構造がそのままアプリケーションのルーティング構造に反映される ファイルシステムベースルーティング を採用しています。

この方式では、app​/​routes​/​about.tsx というファイルを作成すれば、自動的に ​/​about というパスでアクセスできるページが生成されます。コード上でルート定義を行う必要がなく、視覚的にルーティング構造を把握できるのが最大の利点です。

Remix ルーティングの特徴

Remix のルーティングには、以下のような独自の特徴があります。

  • ネストレイアウト: 親ルートのレイアウトを継承しながら子ルートをレンダリング
  • 可変パラメータ: $ 記号を使った動的セグメント(例: $userId
  • パスなしレイアウト: URL に影響せず、レイアウトだけを共有する _ プレフィックス
  • Splat ルート: $ で残りの全パスをキャッチ
  • モーダル/並列ルート: 同じ URL で複数のコンポーネントを表示

これらのパターンを組み合わせることで、複雑な UI 構造も柔軟に実現できます。

以下の図は、Remix のファイル構造が URL パスとどう対応するかを示しています。

mermaidflowchart TD
  routes["app/routes/"] --> index["index.tsx<br/>→ /"]
  routes --> about["about.tsx<br/>→ /about"]
  routes --> blog["blog.tsx<br/>→ /blog"]
  blog --> blogIndex["blog._index.tsx<br/>→ /blog"]
  blog --> blogPost["blog.$postId.tsx<br/>→ /blog/:postId"]
  routes --> user["users.$userId.tsx<br/>→ /users/:userId"]

この図から分かるように、ファイル名の . がパスの ​/​ に、$ が動的パラメータに変換されます。

課題

ルーティングパターンの複雑さ

Remix のファイルベースルーティングは直感的ですが、以下のような課題があります。

  • ネストが深くなると命名規則が分かりにくい: blog.posts.$postId.edit.tsx のようなファイル名が長くなる
  • 特殊記号の意味を覚える必要がある: $_() などの記号が持つ役割を理解していないと混乱する
  • レイアウト共有とパス構造の関係が複雑: パスなしレイアウトを使う場合と使わない場合で、ファイル配置が大きく変わる
  • モーダルや並列ルートの実装方法が分かりにくい: 同じ URL で複数のコンポーネントをどう表示するか

これらの課題により、実装時に「このファイル名で本当に正しいのか?」と不安になったり、試行錯誤に時間がかかったりすることが多くなります。

以下の図は、ファイル名の記号とその役割を整理したものです。

mermaidflowchart LR
  file["ファイル名記号"] --> dot["「.」<br/>パス区切り"]
  file --> dollar["「$」<br/>動的パラメータ"]
  file --> underscore["「_」<br/>パスなしレイアウト"]
  file --> paren["「()」<br/>グループ化"]

  dot --> dotEx["/blog/posts"]
  dollar --> dollarEx["/:userId/:postId"]
  underscore --> underEx["URL に影響なし"]
  paren --> parenEx["整理用"]

情報の散在

公式ドキュメントには各機能の説明がありますが、実際のファイル構造と URL の対応を一覧で確認できる資料は少なく、複数のページを行き来しながら理解する必要があります。

特に以下のような情報が欲しい場面で困ることが多いでしょう。

  • 「この URL 構造を実現するには、どんなファイル名にすればいい?」
  • 「このファイル名だと、どんな URL でアクセスできる?」
  • 「ネストレイアウトと可変パラメータを組み合わせるには?」

解決策

ルーティングパターンの体系化

この記事では、Remix の主要なルーティングパターンを 機能別・構造別 に分類し、以下の情報を一覧表でまとめました。

  • ファイルパス: app​/​routes 内のファイル配置
  • URL パス: アクセス可能な URL
  • パラメータ: 動的パラメータの受け取り方
  • 用途・説明: そのパターンの使いどころ

一覧表を参照すれば、目的のルーティング構造を実現するためのファイル名がすぐに分かります。

記号の役割の明確化

Remix のルーティングで使われる記号とその役割を以下にまとめます。

記号役割
.パスの区切り(​/​ に変換される)blog.posts.tsx​/​blog​/​posts
$動的パラメータ$userId.tsx​/​:userId
_パスなしレイアウト_layout.tsx → URL に影響なし
()グループ化(整理用)(auth).login.tsx​/​login
_indexインデックスルートblog._index.tsx​/​blog
$.tsxSplat ルート(全パスキャッチ)$.tsx​/​any​/​path​/​here

これらの記号を組み合わせることで、柔軟なルーティング構造を構築できます。

以下の図は、記号を組み合わせた実例を示しています。

mermaidflowchart TD
  example["ファイル名例"]
  example --> ex1["blog.posts.$postId.edit.tsx"]
  example --> ex2["_app.dashboard.tsx"]
  example --> ex3["users.$userId.$.tsx"]

  ex1 --> ex1Path["/blog/posts/:postId/edit"]
  ex2 --> ex2Path["/dashboard<br/>(レイアウト共有)"]
  ex3 --> ex3Path["/users/:userId/*"]

早見表の提供

次のセクションでは、以下のカテゴリに分けて早見表を提供します。

  1. 基本ルート: 静的パス、インデックスルート
  2. 動的ルート: 可変パラメータ、複数パラメータ
  3. ネストルート: レイアウト共有、深いネスト
  4. パスなしレイアウト: _ プレフィックスを使ったレイアウト共有
  5. Splat ルート: 残りの全パスをキャッチ
  6. モーダル・並列ルート: URL を変えずに複雑な UI を実現

具体例

基本ルート

基本的な静的パスとインデックスルートのパターンです。

#ファイルパスURL パス説明
1app​/​routes​/​_index.tsx​/​トップページ
2app​/​routes​/​about.tsx​/​about単一の静的ページ
3app​/​routes​/​contact.tsx​/​contact単一の静的ページ
4app​/​routes​/​blog._index.tsx​/​blog​/​blog のインデックスページ
5app​/​routes​/​blog.posts.tsx​/​blog​/​posts​/​blog​/​posts ページ

コード例:トップページ

app​/​routes​/​_index.tsx はサイトのトップページを定義します。

typescript// app/routes/_index.tsx
import type { MetaFunction } from '@remix-run/node';

export const meta: MetaFunction = () => {
  return [
    { title: 'ホーム - My Remix App' },
    {
      name: 'description',
      content: 'Remix アプリのトップページです',
    },
  ];
};

メタ情報とローダー

メタ情報を定義し、SEO に最適化します。

typescriptexport default function Index() {
  return (
    <div>
      <h1>ようこそ、My Remix App へ</h1>
      <p>Remix のルーティングを活用したサイトです。</p>
    </div>
  );
}

コード例:静的ページ

app​/​routes​/​about.tsx​/​about にアクセスできる静的ページです。

typescript// app/routes/about.tsx
export default function About() {
  return (
    <div>
      <h1>About Us</h1>
      <p>このサイトについて説明します。</p>
    </div>
  );
}

動的ルート

URL パラメータを受け取る動的ルートのパターンです。

#ファイルパスURL パスパラメータ説明
6app​/​routes​/​users.$userId.tsx​/​users​/​:userIduserIdユーザー詳細ページ
7app​/​routes​/​posts.$postId.tsx​/​posts​/​:postIdpostId投稿詳細ページ
8app​/​routes​/​products.$productId.tsx​/​products​/​:productIdproductId商品詳細ページ
9app​/​routes​/​blog.$slug.tsx​/​blog​/​:slugslugブログ記事ページ
10app​/​routes​/​users.$userId.edit.tsx​/​users​/​:userId​/​edituserIdユーザー編集ページ

コード例:ユーザー詳細ページ

app​/​routes​/​users.$userId.tsx でユーザー ID を受け取り、データを表示します。

typescript// app/routes/users.$userId.tsx
import {
  json,
  type LoaderFunctionArgs,
} from '@remix-run/node';
import { useLoaderData } from '@remix-run/react';

// ローダーでパラメータを受け取る
export const loader = async ({
  params,
}: LoaderFunctionArgs) => {
  const userId = params.userId;

  // ユーザーデータの取得(実際は DB や API から取得)
  const user = {
    id: userId,
    name: '山田太郎',
    email: 'taro@example.com',
  };

  return json({ user });
};

コンポーネントでのデータ表示

ローダーから取得したデータを表示します。

typescriptexport default function UserDetail() {
  const { user } = useLoaderData<typeof loader>();

  return (
    <div>
      <h1>{user.name} のプロフィール</h1>
      <p>ID: {user.id}</p>
      <p>Email: {user.email}</p>
    </div>
  );
}

ネストルート

親子関係を持つルートで、レイアウトを共有するパターンです。

#ファイルパスURL パスパラメータ説明
11app​/​routes​/​blog.tsx​/​blog-親レイアウト(Outlet で子を表示)
12app​/​routes​/​blog._index.tsx​/​blog-​/​blog のインデックス
13app​/​routes​/​blog.posts.tsx​/​blog​/​posts-ブログ投稿一覧
14app​/​routes​/​blog.posts.$postId.tsx​/​blog​/​posts​/​:postIdpostIdブログ投稿詳細
15app​/​routes​/​blog.posts.$postId.edit.tsx​/​blog​/​posts​/​:postId​/​editpostIdブログ投稿編集

コード例:親レイアウト

app​/​routes​/​blog.tsx​/​blog 配下のすべてのページに共通するレイアウトを定義します。

typescript// app/routes/blog.tsx
import { Outlet } from '@remix-run/react';

export default function BlogLayout() {
  return (
    <div>
      <header>
        <h1>ブログ</h1>
        <nav>
          <a href='/blog'>トップ</a> |{' '}
          <a href='/blog/posts'>記事一覧</a>
        </nav>
      </header>
      <main>
        {/* 子ルートがここに表示される */}
        <Outlet />
      </main>
      <footer>© 2025 My Blog</footer>
    </div>
  );
}

Outlet の役割

<Outlet ​/​> は子ルートのコンポーネントをレンダリングする場所を示します。

コード例:子ルート

app​/​routes​/​blog.posts.$postId.tsx は親レイアウト内に表示されます。

typescript// app/routes/blog.posts.$postId.tsx
import {
  json,
  type LoaderFunctionArgs,
} from '@remix-run/node';
import { useLoaderData } from '@remix-run/react';

export const loader = async ({
  params,
}: LoaderFunctionArgs) => {
  const postId = params.postId;
  const post = {
    id: postId,
    title: 'Remix 入門',
    content: 'Remix の使い方を解説します。',
  };
  return json({ post });
};

データの表示

typescriptexport default function BlogPost() {
  const { post } = useLoaderData<typeof loader>();

  return (
    <article>
      <h2>{post.title}</h2>
      <p>{post.content}</p>
    </article>
  );
}

以下の図は、ネストルートのレンダリング構造を示しています。

mermaidflowchart TD
  root["blog.tsx<br/>(親レイアウト)"] --> outlet["Outlet"]
  outlet --> index["blog._index.tsx<br/>/blog"]
  outlet --> posts["blog.posts.$postId.tsx<br/>/blog/posts/:postId"]
  outlet --> edit["blog.posts.$postId.edit.tsx<br/>/blog/posts/:postId/edit"]

パスなしレイアウト

URL に影響を与えずにレイアウトを共有する _ プレフィックスのパターンです。

#ファイルパスURL パス説明
16app​/​routes​/​_app.tsx-レイアウトのみ(URL に現れない)
17app​/​routes​/​_app.dashboard.tsx​/​dashboard_app レイアウトを継承
18app​/​routes​/​_app.settings.tsx​/​settings_app レイアウトを継承
19app​/​routes​/​_auth.tsx-認証レイアウト
20app​/​routes​/​_auth.login.tsx​/​loginログインページ(_auth レイアウトを継承)
21app​/​routes​/​_auth.register.tsx​/​register登録ページ(_auth レイアウトを継承)

コード例:パスなしレイアウト

app​/​routes​/​_app.tsx は URL に現れませんが、レイアウトを提供します。

typescript// app/routes/_app.tsx
import { Outlet } from '@remix-run/react';

export default function AppLayout() {
  return (
    <div>
      <aside>
        <nav>
          <a href='/dashboard'>ダッシュボード</a>
          <a href='/settings'>設定</a>
        </nav>
      </aside>
      <main>
        <Outlet />
      </main>
    </div>
  );
}

子ルートの定義

app​/​routes​/​_app.dashboard.tsx​/​dashboard でアクセスでき、_app レイアウトを継承します。

typescript// app/routes/_app.dashboard.tsx
export default function Dashboard() {
  return (
    <div>
      <h1>ダッシュボード</h1>
      <p>アプリのメイン画面です。</p>
    </div>
  );
}

コード例:認証レイアウト

app​/​routes​/​_auth.tsx はログイン・登録ページで共有されるレイアウトです。

typescript// app/routes/_auth.tsx
import { Outlet } from '@remix-run/react';

export default function AuthLayout() {
  return (
    <div
      style={{
        display: 'flex',
        justifyContent: 'center',
        alignItems: 'center',
        height: '100vh',
      }}
    >
      <div
        style={{
          border: '1px solid #ccc',
          padding: '2rem',
          borderRadius: '8px',
        }}
      >
        <Outlet />
      </div>
    </div>
  );
}

ログインページ

typescript// app/routes/_auth.login.tsx
export default function Login() {
  return (
    <form>
      <h2>ログイン</h2>
      <input type='email' placeholder='メールアドレス' />
      <input type='password' placeholder='パスワード' />
      <button type='submit'>ログイン</button>
    </form>
  );
}

以下の図は、パスなしレイアウトの構造を示しています。

mermaidflowchart TD
  auth["_auth.tsx<br/>(URL に現れない)"] --> login["_auth.login.tsx<br/>/login"]
  auth --> register["_auth.register.tsx<br/>/register"]

  app["_app.tsx<br/>(URL に現れない)"] --> dashboard["_app.dashboard.tsx<br/>/dashboard"]
  app --> settings["_app.settings.tsx<br/>/settings"]

Splat ルート

URL の残りの全パスをキャッチする Splat ルートのパターンです。

#ファイルパスURL パスパラメータ説明
22app​/​routes​/​$.tsx​/​**すべてのパスをキャッチ
23app​/​routes​/​docs.$.tsx​/​docs​/​**​/​docs 配下の全パスをキャッチ
24app​/​routes​/​files.$path.$.tsx​/​files​/​:path​/​*path, *複数セグメントをキャッチ

コード例:404 ページ

app​/​routes​/​$.tsx は存在しないパスをすべてキャッチし、404 ページを表示します。

typescript// app/routes/$.tsx
import { useParams } from '@remix-run/react';

export default function NotFound() {
  const params = useParams();

  return (
    <div>
      <h1>404 - ページが見つかりません</h1>
      <p>
        お探しのページ <code>/{params['*']}</code>{' '}
        は存在しません。
      </p>
    </div>
  );
}

コード例:ドキュメントルート

app​/​routes​/​docs.$.tsx​/​docs 配下のすべてのパスで同じコンポーネントを表示します。

typescript// app/routes/docs.$.tsx
import { useParams } from '@remix-run/react';

export default function Docs() {
  const params = useParams();
  const docPath = params['*'] || 'index';

  return (
    <div>
      <h1>ドキュメント: {docPath}</h1>
      <p>このパスのドキュメントを表示します。</p>
    </div>
  );
}

モーダル・並列ルート

同じ URL で複数のコンポーネントを表示する、モーダルや並列ルートのパターンです。

#ファイルパスURL パス説明
25app​/​routes​/​photos.tsx​/​photos写真一覧ページ
26app​/​routes​/​photos.$photoId.modal.tsx​/​photos​/​:photoIdモーダルで写真詳細を表示
27app​/​routes​/​products.tsx​/​products商品一覧ページ
28app​/​routes​/​products.$productId.modal.tsx​/​products​/​:productIdモーダルで商品詳細を表示

コード例:写真一覧ページ

app​/​routes​/​photos.tsx は写真一覧を表示します。

typescript// app/routes/photos.tsx
import { Link, Outlet } from '@remix-run/react';

export default function Photos() {
  const photos = [
    { id: '1', title: '風景 1' },
    { id: '2', title: '風景 2' },
    { id: '3', title: '風景 3' },
  ];

  return (
    <div>
      <h1>写真一覧</h1>
      <div style={{ display: 'flex', gap: '1rem' }}>
        {photos.map((photo) => (
          <Link key={photo.id} to={`/photos/${photo.id}`}>
            <img
              src={`/images/${photo.id}.jpg`}
              alt={photo.title}
              style={{ width: '200px' }}
            />
          </Link>
        ))}
      </div>
      {/* モーダルがここに表示される */}
      <Outlet />
    </div>
  );
}

コード例:モーダル表示

app​/​routes​/​photos.$photoId.modal.tsx はモーダルで写真詳細を表示します。

typescript// app/routes/photos.$photoId.modal.tsx
import {
  json,
  type LoaderFunctionArgs,
} from '@remix-run/node';
import {
  useLoaderData,
  useNavigate,
} from '@remix-run/react';

export const loader = async ({
  params,
}: LoaderFunctionArgs) => {
  const photoId = params.photoId;
  const photo = {
    id: photoId,
    title: `風景 ${photoId}`,
    url: `/images/${photoId}.jpg`,
  };
  return json({ photo });
};

モーダルコンポーネント

typescriptexport default function PhotoModal() {
  const { photo } = useLoaderData<typeof loader>();
  const navigate = useNavigate();

  return (
    <div
      style={{
        position: 'fixed',
        top: 0,
        left: 0,
        width: '100%',
        height: '100%',
        backgroundColor: 'rgba(0, 0, 0, 0.8)',
        display: 'flex',
        justifyContent: 'center',
        alignItems: 'center',
      }}
      onClick={() => navigate('/photos')}
    >
      <div
        style={{
          backgroundColor: 'white',
          padding: '2rem',
          borderRadius: '8px',
        }}
      >
        <h2>{photo.title}</h2>
        <img
          src={photo.url}
          alt={photo.title}
          style={{ maxWidth: '600px' }}
        />
      </div>
    </div>
  );
}

以下の図は、モーダルルートの動作を示しています。

mermaidsequenceDiagram
  participant User as ユーザー
  participant List as photos.tsx<br/>(一覧)
  participant Modal as photos.$photoId.modal.tsx<br/>(モーダル)

  User->>List: /photos にアクセス
  List->>User: 写真一覧を表示
  User->>Modal: 写真をクリック<br/>/photos/:photoId
  Modal->>User: モーダルで詳細表示
  User->>List: モーダル外をクリック
  List->>User: 一覧に戻る

複雑なパラメータパターン

複数の動的パラメータを組み合わせたパターンです。

#ファイルパスURL パスパラメータ説明
29app​/​routes​/​users.$userId.posts.$postId.tsx​/​users​/​:userId​/​posts​/​:postIduserId, postIdユーザーの投稿詳細
30app​/​routes​/​orgs.$orgId.projects.$projectId.tsx​/​orgs​/​:orgId​/​projects​/​:projectIdorgId, projectId組織のプロジェクト詳細
31app​/​routes​/​stores.$storeId.products.$productId.tsx​/​stores​/​:storeId​/​products​/​:productIdstoreId, productId店舗の商品詳細

コード例:複数パラメータの取得

app​/​routes​/​users.$userId.posts.$postId.tsx で複数のパラメータを受け取ります。

typescript// app/routes/users.$userId.posts.$postId.tsx
import {
  json,
  type LoaderFunctionArgs,
} from '@remix-run/node';
import { useLoaderData } from '@remix-run/react';

export const loader = async ({
  params,
}: LoaderFunctionArgs) => {
  const { userId, postId } = params;

  // ユーザーと投稿のデータ取得
  const user = { id: userId, name: '山田太郎' };
  const post = {
    id: postId,
    title: 'Remix の使い方',
    content: 'Remix について解説します。',
  };

  return json({ user, post });
};

データの表示

typescriptexport default function UserPost() {
  const { user, post } = useLoaderData<typeof loader>();

  return (
    <div>
      <h1>{user.name} の投稿</h1>
      <article>
        <h2>{post.title}</h2>
        <p>{post.content}</p>
      </article>
    </div>
  );
}

図で理解できる要点

  • ファイル名の . は URL パスの ​/​ に変換される
  • $ は動的パラメータを示し、params で取得できる
  • _ プレフィックスは URL に影響せず、レイアウトのみを提供する
  • ネストルートでは親の <Outlet ​/​> に子コンポーネントがレンダリングされる
  • Splat ルート($.tsx)は残りの全パスをキャッチする

まとめ

Remix のファイルシステムベースルーティングは、直感的でありながら非常に柔軟な設計が特徴です。この記事では、基本的な静的パスから、動的パラメータ、ネストレイアウト、パスなしレイアウト、Splat ルート、モーダルルートまで、主要なパターンを網羅的に解説しました。

ファイル名の記号(.$_)の役割を理解し、早見表を参照することで、どんなルーティング要件にも素早く対応できるようになります。特に以下のポイントを押さえておくと、実装がスムーズに進むでしょう。

  • ネストレイアウト: 親ルートで <Outlet ​/​> を配置し、子ルートを表示する
  • 動的パラメータ: $ を使い、params で値を取得する
  • パスなしレイアウト: _ プレフィックスで URL に影響しないレイアウトを共有する
  • Splat ルート: $.tsx で柔軟なパスキャッチを実現する

Remix のルーティングをマスターすることで、複雑な UI 構造も簡潔に実装でき、保守性の高いアプリケーションを構築できます。ぜひこの早見表を活用して、効率的な開発を進めてくださいね。

関連リンク