Remix の SPA モードで作ったフロントエンドを AWS Amplify Gen 2 につなぐ構成は、2024 年の記事のまま組むと 3 か所で止まります。Gen 1 の amplify init は新規アカウントで通らず、npm i remix で入るのは React を使わない Remix 3 になり、Amplify Hosting は amplify_outputs.json が無いままフロントをビルドして失敗します。この記事では 2026 年 10 月時点の公開版と React Router 8.4.0 での実測をもとに、接続コード、amplify.yml、リライト規則、この構成を選ばない方がよい条件までを順に示します。
まとめ:Remix SPAモードとAmplify Gen 2を組む前に決める5点
| 決めること | 2026年10月時点の結論 | 理由 |
|---|---|---|
| フロントの系列 | React Router v8(8.4.0) | v2 の後継。remix 3.0.0 は別系列 |
| バックエンドの世代 | Gen 2 一択 | Gen 1 は新規受付停止・2027-05-01 EOL |
| データ取得 | clientLoader / clientAction | root 以外の loader はビルドで失敗 |
| Hosting のビルド | backend で pipeline-deploy | outputs をビルド前に生成 |
| 配信とリライト | build/client + 拡張子除外の200 | build/server は配信しない |
この 5 点が決まれば、残りは設定ファイルの書き方だけです。以下、それぞれの根拠とコードを示します。
2026年10月時点の前提:Remix 3正式版とAmplify Gen 1の新規停止
Remix 2.5.0 で SPA モードが入ったのが 2024 年 1 月 11 日、Amplify Gen 2 の一般提供が 2024 年 5 月 6 日です。この組み合わせの解説が増えたのはその直後で、両端ともその後に大きく動きました。
remix 3.0.0とReact Router v8の系列の違い
npm の remix パッケージは 2026 年 10 月 1 日に 3.0.0 が latest になりました。依存 48 件はすべて @remix-run/* で React を含まず、engines は node>=24.3.0 です。Remix 3 は独自のコンポーネントモデルを持つ別系列で、SPA モードも ssr:false もありません。中身はRemix 3のコンポーネントとは?3.0正式版のhandle・mixの書き方と@remix-run/uiで扱っています。
v2 のアプリは @remix-run/react や @remix-run/dev に依存しているため、勝手に 3.0.0 へ上がることはありません。確認時点の v2 系の公開版は 2.17.5 です。v2 の実質的な後継は React Router で、react-router の latest は 8.4.0(2026 年 9 月 15 日)、v7 系は 7.18.4 です。v8 は engines が node>=22.22.0、peerDependencies が react>=19.2.7 なので、Node.js 22.0〜22.21 で動いている CI はフロントを上げた時点で止まります。新規なら npx create-react-router@latest から始めてください。SPA モードの仕様と v2 からの移行判断はRemix SPAモードとは?ssr:falseの設定と制約、React Router移行の判断にまとめています。
Amplify Gen 1の新規受付停止とamplify initの拒否
AWS は Amplify CLI のリポジトリで Gen 1 の扱いを告知しています。「Starting May 1, 2026, Gen 1 backends will receive only critical bug fixes and security patches and will reach end of life on May 1, 2027.」(aws-amplify/amplify-cli issue #14881、2026 年 5 月 14 日)。
紛らわしいのは、@aws-amplify/cli 14.5.1(2026 年 6 月 25 日)が npm に deprecated の印なしで残っていることです。インストールはできます。ただし同梱の @aws-amplify/[email protected] に lib/gen1-new-customer-restriction.js が入っており、CLI に設定したリージョンの Amplify アプリを走査して backend environment が 1 つも無ければ「AWS Amplify Gen 1 has entered maintenance mode and will no longer accept new customers.」で amplify init を中断します。Gen 1 を使ったことがないアカウントでは、選ぼうとしても初期化できません。
Gen 1の記事からGen 2へ置き換えるコマンドとインポート
2024 年前半の記事に載っているコマンドと、Gen 2 での対応物です。対応付けは AWS のGen 2 移行フィーチャーマトリクスに沿っています。
| 用途 | Gen 1(旧) | Gen 2(現行) |
|---|---|---|
| プロジェクト作成 | amplify init |
npm create amplify@latest |
| 認証の追加 | amplify add auth |
amplify/auth/resource.ts |
| 環境の管理 | amplify env add ほか |
Git ブランチ |
| 開発者ごとの検証 | amplify mock |
npx ampx sandbox |
| 本番へ反映 | amplify push |
npx ampx pipeline-deploy |
| 接続情報ファイル | aws-exports.js |
amplify_outputs.json |
取り違えやすいのは 4 行目と 5 行目です。ampx sandbox は amplify/ の変更を監視して開発者個人のスタックへ反映し続けるコマンドで、公式は「Cloud sandbox environments are designed for development purposes and are not intended for production workloads.」と書いています。amplify push の置き換え先は pipeline-deploy か Amplify Hosting の自動ビルドです。Gen 2 の CLI 全体はAWS Amplify Gen 2の使い方:ampxで初期化からCI/CDまで【2026年版】、Gen 1 と Gen 2 の設計の違いと料金はAWS Amplifyとは?Gen 2のバックエンド定義と料金・採用判断を実装者目線で解説で扱っています。
Amplify JavaScript v5.0.0でのデフォルトインポート廃止
古いサンプルの import Amplify from 'aws-amplify' は現行版では動きませんが、これは Gen 1 と Gen 2 の違いではありません。unpkg で公開版を見ると、デフォルトエクスポートがあるのは [email protected] までで、型定義に「@deprecated use named import」と付いています。消えたのは 5.0.0(2022 年 11 月 8 日)です。v6 の変更点は import { signIn } from 'aws-amplify/auth' のようなカテゴリ単位のインポートでした。
現行の 6.22.1(2026 年 9 月 28 日)では、6.22.0 で createAmplifyContext と createConfigurationBuilder がトップレベルに加わりました。createAmplifyContext は型定義の説明どおり「not set as the global context」で、リクエストごとに設定を分けたいサーバー側やマルチテナント向けです。サーバーを持たない SPA モードでは、これまでどおり Amplify.configure() を 1 回呼べば足ります。バックエンド側は @aws-amplify/backend 1.25.1、@aws-amplify/backend-cli 1.10.0 で、CLI の engines は ^18.19.0 || ^20.6.0 || >=22 です。Node.js を 22.22 以上にそろえれば React Router v8 とも衝突しません。
React Router v8のSPAモードとAmplify Gen 2の接続設定
ここからのコードは React Router 8.4.0・aws-amplify 6.22.1・Node.js 26.5.0 でビルドを実行して確かめたものです。
ssr:falseでのrootのビルド時描画
SPA モードは react-router.config.ts の 1 行で有効になります。
import { type Config } from "@react-router/dev/config";
export default {
ssr: false,
} satisfies Config;
React Router の SPA ガイドは、ssr:false が止めるのは runtime server rendering だけで「React Router will still server render your root route at build time to generate the index.html file」と書いています。root の初期描画で window に触れるとビルドが落ちます。HydrateFallback に window.innerWidth を書いて 8.4.0 でビルドすると、ReferenceError: window is not defined の後に次のエラーで終了コード 1 になりました。
Error: Prerender: Request failed for /: SPA Mode: Received a 500 status code from `entry.server.tsx` while prerendering your SPA Fallback HTML file.
Amplify UI の Authenticator のように DOM を前提にしやすい部品は、root の HydrateFallback ではなく、ハイドレーション後に描画される子ルートへ置いてください。もう 1 つ、正常なビルドでも build/server/index.js が残りました。配信するのは build/client だけです。
amplify_outputs.jsonの読み込みと.gitignoreの関係
接続情報は Gen 2 が生成する amplify_outputs.json を root モジュールで読み込み、Amplify.configure() に渡します。モジュール直下で呼んでもビルド時の描画は通りました。
// app/root.tsx
import { Links, Meta, Outlet, Scripts, ScrollRestoration } from "react-router";
import { Amplify } from "aws-amplify";
import outputs from "../amplify_outputs.json";
Amplify.configure(outputs);
export function Layout({ children }: { children: React.ReactNode }) {
return (
<html lang="ja">
<head><meta charSet="utf-8" /><Meta /><Links /></head>
<body>{children}<ScrollRestoration /><Scripts /></body>
</html>
);
}
export function HydrateFallback() {
return <p>読み込み中…</p>;
}
export default function Root() {
return <Outlet />;
}
JSON を import するので tsconfig.json に resolveJsonModule: true が要ります。注意したいのは、npm create amplify@latest(create-amplify 1.4.1)が .gitignore に amplify_outputs* を書き込むことです。手元では npx ampx sandbox が生成するので動きますが、リポジトリには入りません。そのまま Amplify Hosting でフロントだけをビルドすると、次のエラーで止まります。
✗ Build failed
Could not resolve "../amplify_outputs.json" from "app/root.tsx"
接続情報をデプロイ先のバックエンドと一致させるため、後述の amplify.yml でフロントのビルド前に生成します。
clientLoaderによるAmplify Dataの取得
以下の clientLoader の例は app/routes/todos.tsx に置きます。標準の構成ではファイルの追加だけではルートにならないため、app/routes.ts で @react-router/dev/routes の route をインポートし、既存の配列へ route("todos", "routes/todos.tsx") を追加してください。
SPA モードでは root 以外のルートに loader を書けません。試しに子ルートへ loader を足すと、8.4.0 は次のメッセージで終了コード 1 を返しました。
[react-router:route-exports] SPA Mode: 1 invalid route export(s) in `routes/todos.tsx`: `loader`. See https://reactrouter.com/how-to/spa for more information.
Amplify Data(AppSync)への問い合わせは clientLoader に置きます。クライアントの作り方はAmplify の Connect to API ページと同じです。
// app/routes/todos.tsx
import { generateClient } from "aws-amplify/data";
import type { Schema } from "../../amplify/data/resource";
import type { Route } from "./+types/todos";
const client = generateClient<Schema>();
export async function clientLoader() {
const { data, errors } = await client.models.Todo.list();
if (errors) throw new Response("Todo の取得に失敗しました", { status: 500 });
return { todos: data };
}
export default function Todos({ loaderData }: Route.ComponentProps) {
return (
<ul>
{loaderData.todos.map((t) => <li key={t.id}>{t.content}</li>)}
</ul>
);
}
root の loader だけは認められていて、ビルド時に 1 回実行されます(HydrateFallback の loaderData で受け取る)。Remix v2 はさらに厳しく、v2 の SPA Mode ガイドは「You cannot use server APIs such as headers, loader, and action -- the build will throw an error if you export them」と root を含めて禁止しています。両系列の解説を混ぜて読むとここで食い違います。clientLoader と clientAction の使い分けはReact Routerのaction・loaderの使い方|clientAction/clientLoaderの違いとv8対応を参照してください。
既定の認可モードと公開前の切り替え
サーバーを通さない構成では、AppSync の認可設定がそのままセキュリティ境界です。create-amplify 1.4.1 が生成する amplify/data/resource.ts は allow.guest() と defaultAuthorizationMode: 'identityPool' で、未認証の Cognito ID プールロールに全操作を許した状態から始まります。公式の Set up data ページの例は allow.publicApiKey()(expiresInDays: 30)で、こちらを写すと API キーがブラウザに配られたまま公開されます。非公開データをユーザープール認証で扱う場合は、個人別データなら allow.owner()、全ログインユーザーで共有するデータなら allow.authenticated() へ切り替え、defaultAuthorizationMode を 'userPool' にしてください。ログイン画面の作り込みはAWS Cognito Hosted UI(マネージドログイン)のカスタマイズ・日本語化と実装手順が参考になります。
Amplify Hostingのamplify.ymlとリライト設定
amplify.yml:backendでpipeline-deploy、成果物はbuild/client
フロントとバックエンドを 1 つのリポジトリに置く構成では、ビルド設定を次のように書きます。backend フェーズの 2 行は Amplify のマルチリポジトリ解説に載っている形そのままで、$AWS_BRANCH と $AWS_APP_ID は Amplify Hosting がビルド環境に入れる変数です。
version: 1
backend:
phases:
build:
commands:
- npm ci --cache .npm --prefer-offline
- npx ampx pipeline-deploy --branch $AWS_BRANCH --app-id $AWS_APP_ID
frontend:
phases:
build:
commands:
- npm run build
artifacts:
baseDirectory: build/client
files:
- '**/*'
cache:
paths:
- .npm/**/*
- node_modules/**/*
この順番に意味があります。@aws-amplify/backend-cli 1.10.0 の pipeline-deploy は、バックエンドをデプロイした後に amplify_outputs.json を書き出す実装で、出力先は --outputs-out-dir を省くと「current process working directory」、つまりリポジトリ直下です。frontend の npm run build はその後に走るので、前の節の import が解決します。--branch と --app-id はどちらも必須で、CI 環境と判定されないと「It looks like this command is being run outside of a CI/CD workflow.」で止まります。
baseDirectory を build にすると、SPA モードでも残る build/server/index.js まで公開領域に上がります。必ず build/client を指定してください。バックエンドを別リポジトリにする場合は、backend フェーズを npx ampx generate outputs --branch main --app-id <バックエンドのアプリID> に替えて取得だけにします。
拡張子を除外したSPAフォールバックの200リライト
直接開いた URL を index.html へ返すリライトが要ります。ここで /<*> の catch-all を単独で置くと、.js や .css まで index.html に書き換わってアプリが起動しません。AWS の SPA 向けの例は拡張子を除外した正規表現です。
送信元: </^[^.]+$|\.(?!(css|gif|ico|jpg|js|png|txt|svg|woff|woff2|ttf|map|json|webp)$)([^.]+$)/>
送信先: /index.html
種類 : 200
Amplify のリダイレクトは「Redirects are applied from the top of the list down」と上から順に評価され、実ファイルを優先するという規定はありません。トップからの画面遷移は動くのに、同じ URL を直接開くと 404 になるなら、まずこの規則の有無と順番を確かめてください。
prerender併用時のSPAフォールバックの配信先
一部のページだけ HTML を事前生成したいときは、ssr: false のまま prerender を足せます。React Router の pre-rendering ガイドのとおり、事前生成するパスのルートは loader を持てます。ただし / を含めるとフォールバックのファイル名が変わります。prerender: ["/"] で 8.4.0 をビルドした結果がこれです。
Prerender (html): / -> build/client/index.html
Prerender (html): SPA Fallback -> build/client/__spa-fallback.html
この場合、上の正規表現の送信先は /__spa-fallback.html に替えます。/ も正規表現に一致するので、その上に「/ → /index.html(200)」を置いて事前生成した HTML を守ります。他の事前生成パスも同じで、正規表現より上に個別の規則を足し、デプロイ後に curl で本文入りの HTML が返るかを確かめてください。
loader を持つパスを事前生成すると、クライアント遷移用のデータも .data ファイルとして出力されます。/about を追加してビルドすると build/client/about.data が生成され、AWS の正規表現はこの /about.data にも一致しました(Node.js で照合)。そのままでは画面遷移時のデータ要求まで HTML に書き換わるので、prerender を使うときは除外リストの末尾に data を足します。
送信元: </^[^.]+$|\.(?!(css|gif|ico|jpg|js|png|txt|svg|woff|woff2|ttf|map|json|webp|data)$)([^.]+$)/>
送信先: /__spa-fallback.html
種類 : 200
SSRが必要になったときのdeploy-manifest自作
Amplify Hosting の SSR 対応でアダプタなしに動くのは Next.js だけで、Nuxt は preset アダプタ、Astro と SvelteKit はコミュニティアダプタが前提です。Remix と React Router はこの一覧にありません。SSR で動かすなら .amplify-hosting/static、.amplify-hosting/compute/default、deploy-manifest.json を自前で組み、compute 側でポート 3000 の Node.js サーバーを起動します。
ここで古い解説をそのまま写すと manifest で落ちます。日本語で読める Remix の手順には "runtime": "nodejs18.x" の例が残っていますが、デプロイ仕様の現在の有効値は nodejs20.x・nodejs22.x・nodejs24.x の 3 つだけです。しかも React Router v8 は Node.js 22.22 以上、Remix 3 は 24.3 以上を要求するので、実際に選べるのは v8 なら nodejs22.x か nodejs24.x、Remix 3 なら nodejs24.x だけになります。
SPA モードならこの作業がすべて不要で、compute の料金もかかりません。静的配信なら S3+CloudFront という選択肢もあり、比較はAWS Amplify Hostingとは?料金・特徴とS3+CloudFront/App Runnerとの違いにまとめています。既存の AWS 環境との統合や移行を含めて設計を任せたい場合は、AWSのインフラ構築支援で相談を受け付けています。
この構成を採用すべきでない3つの条件
次のどれかに当てはまるなら、Remix SPA モード+Amplify Gen 2 は選ばないでください。
検索流入が柱になる画面がある。SPA モードの index.html は root しか描画せず、商品詳細や記事本文はクローラが最初に受け取る HTML に入りません。対象ページが固定なら prerender、動的なら SSR を選びます。後者は上の deploy-manifest の自作が前提になるので、Next.js を Amplify に載せる方が手間は少なくなります。
新規プロジェクトで Remix v2 を選ぶ。v2 系の公開版は 2.17.5(2026 年 6 月 1 日)から更新がなく、remix の名前は 3.0.0 に移りました。新規なら React Router v8(Node.js 22.22 以上・React 19.2.7 以上)で組みます。v2 に留まる理由になるのは、既存の v2 アプリを保守している場合だけです。
既存の業務APIを中心に据え、Amplify Dataを追加する必要がない。Amplify Data は AppSync のスキーマを中心に、クライアントから直接呼ぶ設計です。Gen 2 は既存の MySQL・PostgreSQL に接続できるため、RDB の利用だけでは不採用の理由になりません。既存の業務APIで認可やトランザクションを完結でき、AppSyncを追加する利点がない場合は、Amplify Dataを使わず既存APIへ接続する構成を選びます。この場合も Amplify Hosting の静的配信はそのまま使えるので、外すのは Gen 2 のバックエンドだけです。
よくある質問
Remix SPAモードでAmplify Gen 2のデータAPIは使えますか?
使えます。root 以外に loader を書けないため、取得は clientLoader、更新は clientAction に置き、generateClient<Schema>() で作ったクライアントから呼びます。Remix v2 では root も含めて loader を export できません。生成直後のスキーマは allow.guest() で未認証アクセスを許します。非公開の個人別データにはユーザープール認証と allow.owner() を設定し、公開データには公開範囲に応じて操作を制限してください。
Amplify HostingのビルドでCould not resolve amplify_outputs.jsonと出るのはなぜですか?
npm create amplify@latest が .gitignore に amplify_outputs* を入れるため、リポジトリにファイルが無いからです。amplify.yml の backend フェーズに npx ampx pipeline-deploy --branch $AWS_BRANCH --app-id $AWS_APP_ID を書けば、デプロイ後にリポジトリ直下へ生成され、続く frontend のビルドで読み込めます。
Amplify HostingでSPAのURLを直接開くと404になるのはなぜですか?
主な原因は、SPA フォールバックのリライト規則の未設定や、規則の順序・送信先の誤りです。「書き換えとリダイレクト」に、拡張子を除外した正規表現を送信元、/index.html を送信先、種類 200 で追加します。prerender で / を事前生成している場合の送信先は /__spa-fallback.html です。単純な catch-all では JavaScript や CSS まで書き換わり、今度はアプリが起動しなくなります。
Cognitoの認証UIはSPAモードでも動きますか?
動きますが、置き場所に条件があります。root はビルド時に Node.js 上で描画されるため、root の HydrateFallback で window に触れるとビルドが失敗します。@aws-amplify/ui-react(6.15.7)の Authenticator は、ハイドレーション後に描画される子ルートへ置いてください。
これから作るならRemix v2・React Router v8・Remix 3のどれですか?
React Router v8 です。Remix v2 の公開版は 2.17.5 のままで、npm i remix で入る 3.0.0 は React を使わない別系列で SPA モードもありません。React と Amplify UI を使う前提なら、v8 の ssr: false で組むのが 2026 年 10 月時点でいちばん素直な構成です。