数年前までは Next.js 一強だった React 系フレームワークですが、最近では様々な対抗馬が生まれ混沌としてきています。
本記事では自分の思考整理も兼ねて、それぞれのフレームワークの特徴を洗い出したものです。フレームワークの選定に役立てて貰えたなら幸いです。
前提事項
- ここにある全てのフレームワークについて筆者が精通している訳ではありません
- 間違っていたら優しく指摘してください
- React 系フレームワークには React / Preact の他、タイトル詐欺で怒られそうだなと思いつつ React 風の JSX が書けるフレームワークも含めています
- これも怒られるかもしれないので先に書いておきますが、現実的に対抗馬とはなるがフレームワークではないもの(Vite 等)も含めています
- 「SPA」を「サーバーを持たない(静的ファイルのみで構成された)フロントエンドアプリケーション」くらいの意味で使います
Create React App (非推奨)
いきなりフレームワークじゃなくてすみません。
2023年に React 公式ドキュメントが刷新されるまでは、チュートリアルで使用されていたスターターです。
依存パッケージがセキュリティリスクのあるバージョンのまま放置されており、事実上非推奨と考えて良いと思います。一応、プロジェクト作成後に依存パッケージのバージョンをオーバーライドすれば、それ以降は警告を出さずに使えますが、敢えて使う意味も薄いと思います。
Vite / react-ts テンプレート
サーバーを持たない純粋な React を開発するためのツールとして、数年前から注目されているライブラリです。これもフレームワークではありませんが、テンプレートが用意されておりスターターとして使えます。SSR も可能ではありますが、機能が豊富という訳でもないので敢えて採用する意味は薄いと思います。
とにかく開発サーバーの起動・更新が早いのが特徴で、開発体験はかなり良いです。
また、React そのものの機能には口出ししてこないため、開発者にとっては良い意味で薄いラッパーとして使うことができる息の長いツールだと思います。
React には口出ししてきませんが、import周りの拡張はかなり便利で、例えば静的アセットを相対パス等でインポートすると、自動的にパブリック URL を特定して返すような機能があったりします。他にもWebAssembly を良い感じに扱える機能があったり、Bun に対応していたりと、非 Node.js な言語にもしっかり対応しています。
ただし、機能面でほぼ同じことができる後発の Farm の方が、速度面で勝るケースが多いようです。
また、フレームワークとして見た時には、個人的には file-system based なルーティングをしたい時に一手間あったりと、微妙に痒いところに手が届かない部分が気になります。SPA でも Remix の SPA モード等を使えばそのあたりは快適です。
そういった事情から、SPA なら Vite 一択という訳でも無さそうです。
Farm
今年 v1 がリリースされた、Vite よりもさらに早いと謳うビルドツールです。ほとんどの場合、HMR が0.01秒 以内に終わるという脅威のスピードを持ちます。じゃあ起動は遅いのかというとそんなこともなく、モジュールレベルで永続キャッシュが効く上に、初回起動でもページアクセス時にオンデマンドでコンパイルされるようになっているため、どんなに大きいプロジェクトでも1秒以内に起動すると言われています。
またしてもフレームワークではありませんが、こちらも React のスターターが用意されてます。
本体も Rust 製ですが、プラグインも Rust 製のものが公式から出ている他、簡単に Rust 製プラグインを開発できるようになっており、機能数の増加に対してビルド速度が落ちないことが特徴です。Rollup 互換の JavaScript 製プラグインにも対応しており、Vite からの移行もスムーズです。ついでに SWC プラグインにも対応しています。
Bun にも対応していたり、Vite と同様+α なimport 周りの便利機能が使えます。また、Vite は現在、開発と本番でそれぞれ esbuild と Rollup を使い分けているため、その差異によって本番環境のみで起こる不具合が稀にあるようです。Farm では開発時も本番環境でも同じようにビルドされるため、そのような差異が発生しないこともメリットになります。
WebAssembly の取り扱いはまだ Vite の方が便利な気はするのですが、大抵の場合では Farm の方がメリットは大きいと思います。
Next.js
ようやくフレームワークです。Next.js の話をする前に App Router と React Server Components 話をしておきましょう。App Router は React Server Components とその他 Next.js 特有の機能が使えるルーターです。
最初に React Server Components についてです。概要は公式ドキュメントがよく纏まっているので読んでください。
React Server Componentsのメリットとしては以下があります。
- データフェッチをデータベースに近いところで行えるのでページの表示が早い
- 単純な SPA だとフロント取得→ブラウザからデータフェッチと、ブラウザ-サーバー間で最低2往復しないとページコンテンツが表示できない
- 通常の SSR と違って、データフェッチに時間がかかる場合でも Streaming HTML によって先に他のコンテンツを表示できる
- バンドルサイズが抑えられる
- 個人的にはスマホで通信制限中でも無理なくサイトが開けることに魅力を感じています
- フロントエンド側(特にエッジ上)でキャッシュできるので単純な SSR に比べても表示が早い上に、サーバーサイドやデータベースの料金を抑えられる
- ロジックをサーバー上で動く単純な非同期関数として切り出せるため、ユニットテストを行いやすい
React Server Components は「今でも困ってないのに、少しユーザー体験を良くするために過剰な学習コストが必要になる」のように言われることが多いですが、そんなことはないと断言して良い気はします。個人的に App Router は好きで良く使いますが、
- ユーザー体験よりもむしろ開発体験の向上を強く感じる
- Reactだけを書いてきた人からすれば考えることはもちろん増えるので最初は辛いかもしれないが、Pages Router 時代に SSR や ISR を書いてきた人からすれば、むしろ書きやすくなっていると思う
- テストしづらい Hooks を避けやすくなったのも大きい
- 学習コストはドキュメントがしっかりしている分、そこまで高く感じない
- 日本語情報は少ないので、英語ドキュメントを読むのに抵抗がある人が学習コストを高く見積もってしまっている可能性はある
- キャッシュ機構をバックエンド側からフロントエンドに近い位置に持ってくるアプローチなので、フロントエンド単体で見れば手間が増えることもあるが、全体で見れば開発コストもランニングコストも減ることが多そう
という印象を抱いており、過度に怖がらないでほしいとは思っています。Vite + React よりは学習コストは高いでしょうが、それに見合ったメリットがちゃんとあります。
React Server Components のメリットとしてはそんなところで、App Router のメリットとしては以下が挙げられます。
- Parallel Routes や Intercepting Routes 等の SaaS や SNS 開発に便利な独自機能が使える
- HTTPリクエストだけではなく、DBクエリ等もキャッシュできる
- ISRによって、最低限の設定だけで高機能なキャッシュが使える
App Router が Next.jsに導入される以前から ISR は Next.js における人気機能でしたが、App Router 導入後の方が簡単に使えるようになりました。
ISR は以下の仕組みで動作します。
- ISR を設定したページはビルド時にキャッシュされる
- 指定された秒数までは、キャッシュ済みのページを返す
- 指定された秒数を超えてからも最初のアクセス時にはキャッシュ済みのページを返しつつ、バックグラウンドでビルドし直す
- また指定された秒数が経過するまではキャッシュ済みのページを返す
3の手順が肝で、これによってキャッシュが切れても常に高速にページを表示できるようになっています。
アクセス頻度が低いページでは、キャッシュが切れた古すぎるページを返してしまうというデメリットもあるため常に採用した方が良いとは思いませんが、他のキャッシュ戦略と比較しても開発コストが低く気軽に採用できるというメリットが大きいです。サーバー側でのキャッシュに比べても、速度とコスト面でメリットがあります。
そんな訳で
- ページが高速に表示できることが大きな意味を持つ
- DAUやサーバー側の計算コストが大きく、フロント側でのキャッシュできることがコスト面で有効に働く
ようなサービスでは特に Next.js を採用するメリットは大きいと言えます。
また、Route Handlers という API を作成できる機能や、Server Actions (これは React Server Components の機能ですが) というコンポーネント内からサーバー側のコードを簡単に呼び出せる機能によって、フルスタックなアプリケーションも作成できることはメリットになるでしょう。ミドルウェアが設定しづらかったりと若干扱いづらさはありますが、Route Handlers は Web 標準 API を拡張した形式になっているため、Hono を動かすことができます。フルスタックなアプリケーション開発を行う際には、個人的にはこの Hono + Next.js の構成の開発体験が最も良いと思っています。
Remix
Remix もフルスタックなフレームワークです。Vite を使った高速な開発サーバーを使うことができ、開発体験は非常に良いです。また、Remix 自体が Web Fetch API で構成されていることも特徴で、これはすなわち Remix を単なるハンドラとして Express や Hono 上で動かすことができるということです。
また、Remix は file-system based routing を採用しています。他のフレームワークとファイルの命名規則が違うのも特徴ですが、一番の特徴はページファイルにコンポーネントだけではなく、loader や action といった関数を定義することです。
これらの関数を定義して export することによって (React Server Components を使わずとも) サーバー側でのデータ取得や、投稿・更新等を行うことができます。
これらの仕組みはシンプルで分かりやすく、Next.js と比較すると学習コストはかなり低いと思います。
React Server Components はサポートされていませんが、Streaming 周りの機能が最近ではどんどん増えてきており、React Server Components に近いメリットは享受できます。
また、今年には SPA モードが登場し、SPA としてビルドすることも可能になりました。その場合は loader や action をサーバーサイドで実行することはもちろんできませんが、file-system based routing を始めとした様々な恩恵を受けることができます。
一方で、キャッシュ周りは Next.js に比べると実装コストが高いことや、近い将来に React Router との統合が予定されている上、同じタイミングでReact Server Componentsへの対応も行われる可能性があり、バージョンアップコストが高くなりそうなことがデメリットとして挙げられます。
Gatsby
Gatsby はフルスタックなアプリケーションも作成できますが、SSG が得意でどちらかと言えばヘッドレス CMS と組み合わせて HP やブログを作成するのに長けたフレームワークです。
複数のデータソースを1つの GraphQL に統合することに長けていて、大量のコンテンツを見せる必要があるような HP 制作においても、開発体験は非常に高いです。
いわゆる JAMStack と呼ばれる、常に Web サーバーが稼働してなくてもユーザーがコンテンツを見られるようなサイトを作成することができ、WordPress からの乗り換え先として Gatsby + ヘッドレス CMS の構成は長年非常に人気だったように思えます。
ところが、JAMStack は2019年に Jamstack へと名称を変更しました。JAMStack の JAM は JavaScript, API, Markup (HTML) の略でしたが、JavaScript と API は不要、すなわちコンテンツを見せるだけのページを JavaScript を使ってクライアントサイドでレンダリングする必要は必ずしもないだろうという考え方が広まったため、JAM を強調した表記を辞めたようです。
そういった考え方の変化に伴って、Hugo 等の非 JavaScript 系の静的サイトジェネレータや、JSX を使うにしろ Astro のようにレスポンスには JavaScript を含めないフレームワークが流行し、結果として Gatsby のシェアは下がってしまいました。
とはいえ、Gatsby の豊富なプラグインやスターターの強力さは健在で、基本的にはコンテンツを見せるだけだけどコメントやブックマーク機能等で React も使いたいと言った場合には今でも選択肢に入ってくると思います。
Astro
純粋な React のフレームワークではありませんが、React コンポーネントや JSX 構文をサポートしたフレームワークです。特徴として、デフォルトではクライアントに JavaScript を配信しないので、高速にページを表示できます。必要であればクライアントサイドの JavaScript をオンにできますが、インタラクティブな操作が多くほとんどのページで JavaScript を配信する必要があるのであれば、他の React フレームワークの方が高速な開発サーバーやその他独自機能の恩恵を受けられ、相対的に開発体験が向上する可能性が高いです。
とはいえ、React ではなく独自構文を用いた際に使える便利な機能が豊富で、React のフレームワークとして見てしまうとどうしても評価が下がってしまうのはしかたない気はします。どちらにせよインタラクティブな操作が多い場合には他のフレームワークが向いているでしょう。
HP やブログはもちろんのこと、例えば、EC サイトではインタラクティブな操作が少なく、ページ表示が高速なことがそのまま売上に直結することが多く向いているかもしれません。
Waku
React Server Components が使える minimal なフレームワークです。minimal と言いつつ、React Server Components をサポートしているためフルスタックなアプリでも何でも作れます。製作者が日本人なので日本語の情報も得やすいです。
Server Components からしかアクセスできない ./private
ディレクトリを使用することができたりと、React Server Components ならではの独自機能もサポートされており、開発体験もかなり良いと思います。
個人的には Next.js のキャッシュ周りの開発体験が好きなのですが、フロント側でのキャッシュが不要な場合や独自実装が必要な場合で、尚且つ React Server Components が使いたい場合には Waku も選択肢に入ってくると感じました。
Waku の場合は、experimental ではありますが、Node.js で開発しつつ Deno や AWS Lambda 向けにビルドできるため、Next.js よりもデプロイ先の選択肢が広いのも特徴です。
Fresh
Deno 用の フルスタック Preact フレームワークです。Astro と同じく、必要な時以外は JavaScript を配信しない Islands Architecture が採用されていますが、Fresh では React Server Components と同じく、必要な場合には JavaScript を配信するのが前提となっています。
Fresh でも例に漏れず file-system based routing が採用されていますが、_
から始まるファイルへのルーティングは作成されないため、ページファイルのすぐ側でコンポーネントを作成できる等のメリットがあります。
また、_middleware.ts
を配置すると、それが配置されたディレクトリ内の全てのページへのアクセス時にそのミドルウェアが実行されるようになる等、Next.js や Remix と比べるとサーバーサイド側の機能もかなり扱いやすくなっています。
データ取得や投稿は、Remix と似た位置に Next.js と似た書き方をする構成になっています( https://fresh.deno.dev/docs/concepts/data-fetching )。
他にも Partials を使うことで、Server Actions + mutations に近いことができたりと、必要な機能は一通り揃っています。
また、Deno製なのでDeno KV や deno fmt/lint
が使えたりと、他の React フレームワークにはない機能が使えることもメリットです。最近は他のフレームワークでも Deno で動くようになってきていますが、公式にはサポートされていないことが多いです。
少し前までパッケージ管理に難があったため中々 Deno 製フレームワークの導入は進まなかったですが、最近では解消されつつあるため十分に採用できるラインだと思います。
HonoX
Hono + Vite なメタフレームワークです。Hono 単体でも React フレームワークとして使えますが、file-system based routing や Islands Architecture が好きなら HonoX を使った方が良いでしょう。
厳密には React ではなく React っぽい JSX がデフォルトでは動く訳ですが、使った感じでは提供されている Hooks は問題なく動作しそうなため、例えば SolidJS よりはよっぽど React 感があります。しかも、本物の React を動かすことも可能です。
元が Hono なのでメジャーな JavaScript ランタイムならどこでも動かせます。
個人的に一番嬉しい特徴としては、もともとサーバーサイドフレームワークだったためか、特にミドルウェア周りが書きやすいことです。ロギングもアクセス制限も、任意のパスとメソッドに対して自由に書けます。
@hono/zod-validator
なんかが使えるのも嬉しいところです。
Next.js の Route Handler に Hono を置くパターンと比較すると、ISR できなかったり React Server Components が使えなかったりというデメリットはありますが、Hono の JSX は非常に小さいため、インタラクティブなページも高速に初回表示できたり、サーバーサイド側もfile-system based routingできたりといったメリットがあります。
悩みどころですが、やはり ISR がどれくらい必要かといった基準で選定することになりそうです。
まとめ
ここまでをまとめるとこんな感じになります。
○: できる
△: できるけどあんまりやらない/部分的にできる
×: できない/他ライブラリを用いるか多量の独自実装を行わないとできない
フレームワーク | SPA | SSR | React Server Components | file-system based routing | Island Architecture |
---|---|---|---|---|---|
Vite | ○ | △ | × | × | × |
Farm | ○ | △ | × | × | × |
Next.js | △ | ○ | ○ | ○ | × |
Remix | ○ | ○ | × | ○ | × |
Gatsby | △ | △ | × | ○ | × |
Astro | × | ○ | × | ○ | ○ |
Waku | △ | ○ | ○ | ○ | × |
Fresh | × | ○ | × | ○ | ○ |
HonoX | × | ○ | × | ○ | ○ |
つまり、以下の様に選択することになりそうです。
SPAとしてビルドしたい場合
Cloudflare Pages の無制限機能だけで運用したい場合や、CloudFront や Xserver Static 等の静的コンテンツ特化のサービスにホストしたい場合です。この場合は Vite や Farm でも可ですが、file-system based routing が使える Remix の SPA モードで作成すると後悔がないと思います。前述した Remix の React Router 統合対応が面倒そうだと感じるのであれば、次点でビルドが超高速な Farm になると思います。Vite の React テンプレートから始めるメリットは2024年秋現在では薄いでしょう。
フルスタックなアプリケーションを作成したい場合
サーバーサイド機能が豊富な Fresh や HonoX で書くか、Next.js + Hono のような構成にすることになると思います。Express や Hono のハンドラー関数として Remix を動かすこともできますが、HonoX と比較した際にあまりメリットがないので考えないことにします。
Fresh や Next.js + Hono はランタイムの制約があるため、例えば AWS Lambda で動かしたいとなると HonoX を使うしかありません。ランタイムの制約がないのであれば、ISR が使える Next.js + Hono が非常に便利です。Fresh も悪くないフレームワークなのですが、Next.js + Hono 自体は Deno でもある程度動くため、Deno の独自機能が差別化点にならないのが残念なところです。
パフォーマンスを重視したい場合
React Server Components と Island Architecture を採用するメリットはほとんど同じで、ページ表示速度の向上と処理をサーバー側に寄せることによる開発者体験の向上です。
ページ表示速度の向上だけであれば SSR + CDN 等でも解決できます。とはいえ、CDN 周りの自前実装は学習コストこそ低いですが、デバッグ等が面倒で開発体験は悪いです。
そのため、パフォーマンスを重視したい場合には React Server Components か Island Architecture をサポートしたフレームワークを選択することになります。
その中でも、キャッシュ周りが簡単な Next.js が最有力でしょう。ISR の古いページを返してしまう場合がある仕様が要件に合わない場合は、ランタイムにもよりますが Deno KV が事実上のキャッシュシステムとして使えるため、Fresh + Deno KV という構成はかなり使いやすいと思います。
それらがランタイム制約やキャッシュ要件等で使えない場合には、少し前までは Remix を使用するのが流行っていましたが、現在では初期表示速度を落とさずランタイムの選択肢も広い Waku が十分候補に入ってくると思います。多くの SaaS のように、ほぼ全ページで認証情報が必須でキャッシュを使える場面が多くない場合についても、やはり Waku はパフォーマンスを維持できて便利です。
Jamstack 構成にしたい場合
ユーザーのインタラクティブな操作が多い場合には Gatsby、そうでなければ Astro にしましょう。SSG が良い感じにできるフレームワークの選択肢は限られています。
最後に
どうしてもフレームワーク間に(状況によるとはいえ)優劣を付けるようなテーマで筆を取ってしまいましたが、この記事で挙げたフレームワークはどれも完成度が高く、どれを使っても会社は潰れないと思っています。
あくまで個人の意見として受け取ってください。
また、誤りがあれば優しく訂正していただければ即座に修正しますのでよろしくお願いします。