コンテンツへスキップ
ブログに戻る

2023年10月26日 木曜日

Next.js 14

投稿者

Next.js Confで発表した通り、Next.js 14は以下の点を重視したリリースです。

  • Turbopack: App RouterとPages Routerで5,000のテストが合格
    • ローカルサーバー起動が53%高速化
    • Fast Refreshによるコード更新が94%高速化
  • Server Actions (安定版): プログレッシブエンハンスメントによるミューテーション
    • キャッシュと再検証機能と統合
    • シンプルな関数呼び出し、またはフォームとネイティブに連携
  • Partial Prerendering (プレビュー): 高速な初期静的応答 + ストリーミング動的コンテンツ
  • Next.js Learn (新規): App Router、認証、データベースなどを学べる無料コース。

今すぐアップグレードするか、以下から始めましょう

ターミナル
npx create-next-app@latest

Next.js コンパイラ: ターボチャージ

Next.js 13以降、私たちはPages RouterとApp Routerの両方で、Next.jsのローカル開発パフォーマンスを向上させることに取り組んできました。

以前は、この取り組みをサポートするためにnext devやNext.jsの他の部分を書き換えていました。その後、より漸進的なアプローチに変更しました。これは、まずNext.jsのすべての機能をサポートすることに再注力したため、Rustベースのコンパイラが間もなく安定する予定であることを意味します。

私たちの基盤となるRustエンジンであるTurbopackでは、next devの5,000の統合テストが現在合格しています。これらのテストには、7年分のバグ修正と再現が含まれています。

大規模なNext.jsアプリケーションであるvercel.comでテストした結果、以下のことが確認されました。

  • ローカルサーバーの起動が最大53.3%高速化
  • Fast Refreshによるコード更新が最大94.7%高速化

このベンチマークは、大規模なアプリケーション(および大規模なモジュールグラフ)で期待できるパフォーマンス改善の実用的な結果です。next devのテストの90%が現在合格しているため、next dev --turboを使用すると、より高速で信頼性の高いパフォーマンスを一貫して得られるはずです。

テストが100%合格すれば、次のマイナーリリースでTurbopackを安定版に移行します。また、カスタム設定やエコシステムプラグインのためにwebpackの使用も引き続きサポートします。

テストの合格率はareweturboyet.comで確認できます。

フォームとミューテーション

Next.js 9ではAPI Routesが導入され、フロントエンドコードと並行してバックエンドのエンドポイントを迅速に構築できるようになりました。

例えば、api/ディレクトリに新しいファイルを作成します。

pages/api/submit.ts
import type { NextApiRequest, NextApiResponse } from 'next';
 
export default async function handler(
  req: NextApiRequest,
  res: NextApiResponse,
) {
  const data = req.body;
  const id = await createItem(data);
  res.status(200).json({ id });
}

その後、クライアント側では、ReactとonSubmitのようなイベントハンドラを使用して、API Routeへのfetchを行うことができます。

pages/index.tsx
import { FormEvent } from 'react';
 
export default function Page() {
  async function onSubmit(event: FormEvent<HTMLFormElement>) {
    event.preventDefault();
 
    const formData = new FormData(event.currentTarget);
    const response = await fetch('/api/submit', {
      method: 'POST',
      body: formData,
    });
 
    // Handle response if necessary
    const data = await response.json();
    // ...
  }
 
  return (
    <form onSubmit={onSubmit}>
      <input type="text" name="name" />
      <button type="submit">Submit</button>
    </form>
  );
}

Next.js 14では、データミューテーションの作成における開発者体験を簡素化したいと考えています。さらに、ユーザーのネットワーク接続が遅い場合や、低電力デバイスからフォームを送信する場合のユーザー体験を向上させたいと考えています。

Server Actions (安定版)

API Routeを手動で作成する必要がなければどうでしょうか?代わりに、サーバーで安全に実行され、Reactコンポーネントから直接呼び出される関数を定義することができます。

App RouterはReactのcanaryチャネルをベースにしており、これは新しい機能を採用するフレームワークにとって安定しています。v14以降、Next.jsは最新のReact canaryにアップグレードされ、安定版のServer Actionsが含まれています。

Pages Routerの以前の例は、1つのファイルに簡素化できます。

app/page.tsx
export default function Page() {
  async function create(formData: FormData) {
    'use server';
    const id = await createItem(formData);
  }
 
  return (
    <form action={create}>
      <input type="text" name="name" />
      <button type="submit">Submit</button>
    </form>
  );
}

Server Actionsは、過去にサーバー中心のフレームワークを使用したことがある開発者にとっては馴染み深いものに感じるでしょう。フォームやFormData Web APIといったウェブの基本に基づいています。

フォームを介してServer Actionsを使用することはプログレッシブエンハンスメントに役立ちますが、必須ではありません。フォームなしで、関数として直接呼び出すこともできます。TypeScriptを使用する場合、これによりクライアントとサーバー間の完全なエンドツーエンドの型安全性が提供されます。

データの変更、ページの再レンダリング、またはリダイレクトは1回のネットワークラウンドトリップで発生するため、アップストリームプロバイダが遅い場合でも、クライアントに正しいデータが表示されることが保証されます。さらに、異なるアクションを組み合わせて再利用でき、同じルート内で多くの異なるアクションを含めることも可能です。

キャッシュ、再検証、リダイレクトなど

Server ActionsはApp Routerモデル全体に深く統合されています。以下のことができます。

  • revalidatePath()またはrevalidateTag()でキャッシュされたデータを再検証
  • redirect()で別のルートへリダイレクト
  • cookies()でクッキーを設定・読み取り
  • useOptimistic()で楽観的UI更新を処理
  • useFormState()でサーバーからのエラーを捕捉し表示
  • useFormStatus()でクライアント上のロード状態を表示

Server Actionsを使用したフォームとミューテーション、またはServer ComponentsとServer Actionsのセキュリティモデルとベストプラクティスについて詳しく学ぶことができます。

Partial Prerendering (プレビュー)

Next.jsで現在開発中のPartial Prerendering(高速な初期静的応答と動的コンテンツを組み合わせたコンパイラ最適化)のプレビューを共有したいと思います。

Partial Prerenderingは、サーバーサイドレンダリング(SSR)、静的サイト生成(SSG)、およびインクリメンタル静的再検証(ISR)に関する10年間の研究開発に基づいています。

動機

皆様からのフィードバックを伺いました。現在、考慮すべきランタイム、設定オプション、レンダリングメソッドが多すぎます。静的サイトの速度と信頼性を望みつつ、完全に動的でパーソナライズされた応答もサポートしたいと考えていることでしょう。

グローバルな優れたパフォーマンスパーソナライゼーションは、複雑さを犠牲にして実現されるべきではありません。

私たちの課題は、開発者が新しいAPIを学ぶ必要なく、既存のモデルを簡素化することで、より良い開発者体験を創出することでした。サーバーサイドコンテンツの部分的なキャッシュは存在していましたが、これらのアプローチは、私たちが目指す開発者体験とコンポーザビリティの目標を満たす必要がありました。

Partial Prerenderingは、新しいAPIを学ぶ必要がありません。

React Suspense上に構築されています

部分的なプリレンダリングは、Suspenseの境界によって定義されます。その仕組みを説明します。以下のeコマースページを考えてみましょう

app/page.tsx
export default function Page() {
  return (
    <main>
      <header>
        <h1>My Store</h1>
        <Suspense fallback={<CartSkeleton />}>
          <ShoppingCart />
        </Suspense>
      </header>
      <Banner />
      <Suspense fallback={<ProductListSkeleton />}>
        <Recommendations />
      </Suspense>
      <NewProducts />
    </main>
  );
}

部分的なプリレンダリングが有効になっている場合、このページは<Suspense />の境界に基づいて静的なシェルを生成します。React Suspenseのfallbackがプリレンダリングされます。

シェル内のSuspenseフォールバックは、カートを決定するためにクッキーを読み込んだり、ユーザーに基づいてバナーを表示したりするような動的なコンポーネントに置き換えられます。

リクエストが行われると、静的なHTMLシェルが即座に提供されます

<main>
  <header>
    <h1>My Store</h1>
    <div class="cart-skeleton">
      <!-- Hole -->
    </div>
  </header>
  <div class="banner" />
  <div class="product-list-skeleton">
    <!-- Hole -->
  </div>
  <section class="new-products" />
</main>

<ShoppingCart />はユーザーセッションを見るためにcookiesから読み取るため、このコンポーネントは静的シェルと同じHTTPリクエストの一部としてストリーミングされます。追加のネットワークラウンドトリップは不要です。

app/cart.tsx
import { cookies } from 'next/headers'
 
export default function ShoppingCart() {
  const cookieStore = cookies()
  const session = cookieStore.get('session')
  return ...
}

最もきめ細かい静的シェルを持つためには、追加のSuspense境界が必要になる場合があります。ただし、現在loading.jsを使用している場合は、これは暗黙的なSuspense境界であるため、静的シェルを生成するために変更は必要ありません。

近日公開

部分的なプリレンダリングは現在活発に開発中です。今後のマイナーリリースでさらなるアップデートを共有します。

メタデータ改善

ページコンテンツがサーバーからストリーミングされる前に、ビューポート、カラースキーム、テーマに関する重要なメタデータが最初にブラウザに送信される必要があります。

これらのmetaタグが最初のページコンテンツとともに送信されるようにすることで、テーマカラーの変更によるページのちらつきや、ビューポートの変更によるレイアウトのずれを防ぎ、スムーズなユーザーエクスペリエンスに貢献します。

Next.js 14では、ブロッキングメタデータと非ブロッキングメタデータを分離しました。メタデータオプションのごく一部のみがブロッキングであり、非ブロッキングメタデータが部分的にプリレンダリングされたページによる静的シェルの提供を妨げないようにしたいと考えています。

以下のメタデータオプションは現在非推奨であり、将来のメジャーバージョンでmetadataから削除されます

  • viewport: ビューポートの初期ズームおよびその他のプロパティを設定します
  • colorScheme: ビューポートのサポートモード(ライト/ダーク)を設定します
  • themeColor: ビューポート周辺のChromeがレンダリングされるべき色を設定します

Next.js 14からは、これらのオプションを置き換える新しいオプションviewportgenerateViewportが導入されます。その他のmetadataオプションはすべて同じままです。

これらの新しいAPIは本日より導入を開始できます。既存のmetadataオプションは引き続き動作します。

Next.js Learnコース

本日、Next.js Learnで新しい無料コースを公開します。このコースでは以下を学習します

  • Next.js App Router
  • スタイリングとTailwind CSS
  • フォントと画像の最適化
  • レイアウトとページの作成
  • ページ間の移動
  • Postgresデータベースのセットアップ
  • サーバーコンポーネントによるデータフェッチ
  • 静的および動的レンダリング
  • ストリーミング
  • 部分的なプリレンダリング(オプション)
  • 検索とページネーションの追加
  • データの変更
  • エラー処理
  • アクセシビリティの改善
  • 認証の追加
  • メタデータの追加

Next.js Learnは、何百万もの開発者にフレームワークの基礎を教えてきました。この新しい追加機能について、皆様からのフィードバックをお待ちしております。コースを受講するには、nextjs.org/learnにアクセスしてください。

その他の変更点

  • [破壊的変更] Node.jsの最小バージョンが18.17になりました
  • [破壊的変更] next-swcビルドのWASMターゲットを削除しました (PR)
  • [破壊的変更] @next/fontのサポートを終了し、next/fontを優先するように変更しました (Codemod)
  • [破壊的変更] ImageResponseのインポートをnext/serverからnext/ogに変更しました (Codemod)
  • [破壊的変更] next exportコマンドは廃止され、output: 'export'設定が推奨されます (ドキュメント)
  • [非推奨] next/imageonLoadingCompleteは非推奨となり、onLoadに置き換えられます。
  • [非推奨] next/imagedomainsは非推奨となり、remotePatternsに置き換えられます。
  • [機能] fetchキャッシュに関するより詳細なログを有効にできるようになりました (ドキュメント)
  • [改善] 基本的なcreate-next-appアプリケーションの関数サイズが80%削減されました
  • [改善] 開発時にedgeランタイムを使用する際のメモリ管理が強化されました

貢献者

Next.jsは、2,900人以上の個々の開発者、GoogleやMetaのような業界パートナー、そしてVercelのコアチームの共同作業の成果です。GitHub DiscussionsReddit、そしてDiscordでコミュニティに参加してください。

このリリースは、以下のチームの協力によって実現しました。

および、以下の皆様の貢献によるものです: @05lazy, @0xadada, @2-NOW, @aarnadlr, @aaronbrown-vercel, @aaronjy, @abayomi185, @abe1272001, @abhiyandhakal, @abstractvector, @acdlite, @adamjmcgrath, @AdamKatzDev, @adamrhunter, @ademilter, @adictonator, @adilansari, @adtc, @afonsojramos, @agadzik, @agrattan0820, @akd-io, @AkifumiSato, @akshaynox, @alainkaiser, @alantoa, @albertothedev, @AldeonMoriak, @aleksa-codes, @alexanderbluhm, @alexkirsz, @alfred-mountfield, @alpha-xek, @andarist, @Andarist, @andrii-bodnar, @andykenward, @angel1254mc, @anonrig, @anthonyshew, @AntoineBourin, @anujssstw, @apeltop, @aralroca, @aretrace, @artdevgame, @artechventure, @arturbien, @Aryan9592, @AviAvinav, @aziyatali, @BaffinLee, @Banbarashik, @bencmbrook, @benjie, @bennettdams, @bertho-zero, @bigyanse, @Bitbbot, @blue-devil1134, @bot08, @bottxiang, @Bowens20832, @bre30kra69cs, @BrennanColberg, @brkalow, @BrodaNoel, @Brooooooklyn, @brunoeduardodev, @brvnonascimento, @carlos-menezes, @cassidoo, @cattmote, @cesarkohl, @chanceaclark, @charkour, @charlesbdudley, @chibicode, @chrisipanaque, @ChristianIvicevic, @chriswdmr, @chunsch, @ciruz, @cjmling, @clive-h-townsend, @colinhacks, @colinking, @coreyleelarson, @Cow258, @cprussin, @craigwheeler, @cramforce, @cravend, @cristobaldominguez95, @ctjlewis, @cvolant, @cxa, @danger-ahead, @daniel-web-developer, @danmindru, @dante-robinson, @darshanjain-entrepreneur, @darshkpatel, @davecarlson, @David0z, @davidnx, @dciug, @delbaoliveira, @denchance, @DerTimonius, @devagrawal09, @DevEsteves, @devjiwonchoi, @devknoll, @DevLab2425, @devvspaces, @didemkkaslan, @dijonmusters, @dirheimerb, @djreillo, @dlehmhus, @doinki, @dpnolte, @Drblessing, @dtinth, @ducanhgh, @DuCanhGH, @ductnn, @duncanogle, @dunklesToast, @DustinsCode, @dvakatsiienko, @dvoytenko, @dylanjha, @ecklf, @EndangeredMassa, @eps1lon, @ericfennis, @escwxyz, @Ethan-Arrowood, @ethanmick, @ethomson, @fantaasm, @feikerwu, @ferdingler, @FernandVEYRIER, @feugy, @fgiuliani, @fomichroman, @Fonger, @ForsakenHarmony, @franktronics, @FSaldanha, @fsansalvadore, @furkanmavili, @g12i, @gabschne, @gaojude, @gdborton, @gergelyke, @gfgabrielfranca, @gidgudgod, @Gladowar, @Gnadhi, @gnoff, @goguda, @greatSumini, @gruz0, @Guilleo03, @gustavostz, @hanneslund, @HarshaVardhanReddyDuvvuru, @haschikeks, @Heidar-An, @heyitsuzair, @hiddenest, @hiro0218, @hotters, @hsrvms, @hu0p, @hughlilly, @HurSungYun, @hustLer2k, @iamarpitpatidar, @ianldgs, @ianmacartney, @iaurg, @ibash, @ibrahemid, @idoob, @iiegor, @ikryvorotenko, @imranbarbhuiya, @ingovals, @inokawa, @insik-han, @isaackatayev, @ishaqibrahimbot, @ismaelrumzan, @itsmingjie, @ivanhofer, @IvanKiral, @jacobsfletch, @jakemstar, @jamespearson, @JanCizmar, @janicklas-ralph, @jankaifer, @JanKaifer, @jantimon, @jaredpalmer, @javivelasco, @jayair, @jaykch, @Jeffrey-Zutt, @jenewland1999, @jeremydouglas, @JesseKoldewijn, @jessewarren-aa, @jimcresswell, @jiwooIncludeJeong, @jocarrd, @joefreeman, @JohnAdib, @JohnAlbin, @JohnDaly, @johnnyomair, @johnta0, @joliss, @jomeswang, @joostdecock, @Josehower, @josephcsoti, @josh, @joshuabaker, @JoshuaKGoldberg, @joshuaslate, @joulev, @jsteele-stripe, @JTaylor0196, @JuanM04, @jueungrace, @juliusmarminge, @Juneezee, @Just-Moh-it, @juzhiyuan, @jyunhanlin, @kaguya3222, @karlhorky, @kevinmitch14, @keyz, @kijikunnn, @kikobeats, @Kikobeats, @kleintorres, @koba04, @koenpunt, @koltong, @konomae, @kosai106, @krmeda, @kvnang, @kwonoj, @ky1ejs, @kylemcd, @labyrinthitis, @lachlanjc, @lacymorrow, @laityned, @Lantianyou, @leerob, @leodr, @leoortizz, @li-jia-nan, @loettz, @lorenzobloedow, @lubakravche, @lucasassisrosa, @lucasconstantino, @lucgagan, @LukeSchlangen, @LuudJanssen, @lycuid, @M3kH, @m7yue, @manovotny, @maranomynet, @marcus-rise, @MarDi66, @MarkAtOmniux, @martin-wahlberg, @masnormen, @matepapp, @matthew-heath, @mattpr, @maxleiter, @MaxLeiter, @maxproske, @meenie, @meesvandongen, @mhmdrioaf, @michaeloliverx, @mike-plummer, @MiLk, @milovangudelj, @Mingyu-Song, @mirismaili, @mkcy3, @mknichel, @mltsy, @mmaaaaz, @mnajdova, @moetazaneta, @mohanraj-r, @molebox, @morganfeeney, @motopods, @mPaella, @mrkldshv, @mrxbox98, @nabsul, @nathanhammond, @nbouvrette, @nekochantaiwan, @nfinished, @Nick-Mazuk, @nickmccurdy, @niedziolkamichal, @niko20, @nikolovlazar, @nivak-monarch, @nk980113, @nnnnoel, @nocell, @notrab, @nroland013, @nuta, @nutlope, @obusk, @okcoker, @oliviertassinari, @omarhoumz, @opnay, @orionmiz, @ossan-engineer, @patrick91, @pauek, @peraltafederico, @Phiction, @pn-code, @pyjun01, @pythagoras-yamamoto, @qrohlf, @raisedadead, @reconbot, @reshmi-sriram, @reyrodrigez, @ricardofiorani, @rightones, @riqwan, @rishabhpoddar, @rjsdnql123, @rodrigofeijao, @runjuu, @Ryan-Dia, @ryo-manba, @s0h311, @sagarpreet-xflowpay, @sairajchouhan, @samdenty, @samsisle, @sanjaiyan-dev, @saseungmin, @SCG82, @schehata, @Schniz, @sepiropht, @serkanbektas, @sferadev, @ShaunFerris, @shivanshubisht, @shozibabbas, @silvioprog, @simonswiss, @simPod, @sivtu, @SleeplessOne1917, @smaeda-ks, @sonam-serchan, @SonMooSans, @soonoo, @sophiebits, @souporserious, @sp00ls, @sqve, @sreetamdas, @stafyniaksacha, @starunaway, @steebchen, @stefanprobst, @steppefox, @steven-tey, @suhaotian, @sukkaw, @SukkaW, @superbahbi, @SuttonJack, @svarunid, @swaminator, @swarnava, @syedtaqi95, @taep96, @taylorbryant, @teobler, @Terro216, @theevilhead, @thepatrick00, @therealrinku, @thomasballinger, @thorwebdev, @tibi1220, @tim-hanssen, @timeyoutakeit, @tka5, @tknickman, @tomryanx, @trigaten, @tristndev, @tunamagur0, @tvthatsme, @tyhopp, @tyler-lutz, @UnknownMonk, @v1k1, @valentincostam, @valentinh, @valentinpolitov, @vamcs, @vasucp1207, @vicsantizo, @vinaykulk621, @vincenthongzy, @visshaljagtap, @vladikoff, @wherehows, @WhoAmIRUS, @WilderDev, @Willem-Jaap, @williamli, @wiredacorn, @wiscaksono, @wojtekolek, @ws-jm, @wxh06, @wyattfry, @wyattjoh, @xiaolou86, @y-tsubuku, @yagogmaisp, @yangshun, @yasath, @Yash-Singh1, @yigithanyucedag, @ykzts, @Yovach, @yutsuten, @yyuemii, @zek, @zekicaneksi, @zignis, and @zlrlyy