Backブログに戻る

Next.js 9.3

Next.js 9.3では、静的サイト生成の改善、SCSSのネイティブサポート、バンドルサイズの削減、静的404ページなどが導入されました!

本日、Next.js 9.3のリリースを発表できることを嬉しく思います。主な新機能は以下の通りです:

これらの機能はすべて非破壊的で完全な後方互換性があります。更新するには以下を実行するだけです:

Terminal
npm i next@latest react@latest react-dom@latest

次世代静的サイト生成 (SSG) サポート

ウェブサイトやアプリケーションを構築する際、一般的に2つの戦略から選択する必要があります: 静的生成 (SSG) またはサーバーサイドレンダリング (SSR) です。

Next.jsは初のハイブリッドフレームワークで、ページごとに最適な手法を選択できます。

Next.js 9.0では自動静的最適化の概念が導入されました。getInitialPropsのようなブロッキングデータ取得要件がないページは、ビルド時に自動的にHTMLにレンダリングされます。

ブロッキングデータ取得要件があっても、ビルド時にページを静的なHTMLとしてレンダリングしたいケースはさらに多くあります。例としては、(ヘッドレス)コンテンツ管理システム(CMS)で動くマーケティングページや、サイトのブログセクションなどがあります。

私たちはHashiCorpのようなSSGとnext exportのヘビーユーザーと協力し、Next.js史上最もコメントの多いRFCでコミュニティと徹底的に議論を重ね、データ取得と静的生成の新しい統一的な方法を作り上げました。

本日、2つの新しいデータ取得メソッドを発表できることを大変嬉しく思います: getStaticPropsgetServerSideProps。また、動的ルートに対して静的に生成するパラメータを提供する方法として getStaticPaths も含まれています。

これらの新しいメソッドには、getInitialPropsモデルに比べて多くの利点があり、SSGとSSRの違いが明確になっています。

  • getStaticProps (静的生成): ビルド時にデータを取得
  • getStaticPaths (静的生成): データに基づいてプリレンダリングする動的ルートを指定
  • getServerSideProps (サーバーサイドレンダリング): 各リクエストごとにデータを取得
  • これらの改善はAPI追加です。すべての新機能は完全な後方互換性があり、段階的に導入できます。非推奨は導入されておらず、getInitialPropsは現在通り機能し続けます。新しいページやプロジェクトではこれらの新しいメソッドの採用を推奨します。

getStaticProps

ページからgetStaticPropsというasync関数をエクスポートすると、Next.jsはビルド時にこのページをプリレンダリングします。これは特にCMSから特定の静的ページをレンダリングしたい場合に有用です。

getStaticPropsは常にNode.jsコンテキストで実行され、コードは自動的にツリーシェイクされてブラウザバンドルから除外されるため、ブラウザに送信されるコードが少なくなります。これにより、Node.jsとブラウザ環境の間で一貫性のないデータ取得コードの実行について心配する必要がなくなります。

これにより、fetch、REST、GraphQL、または直接データベースにアクセスするなど、あらゆる非同期または同期データ取得技術を使用できます。

pages/posts/[id].js
export async function getStaticProps(context) {
  return {
    props: {}, // ページコンポーネントにpropsとして渡されます
  };
}

contextパラメータは以下のキーを含むオブジェクトです:

  • params: 動的ルートを使用するページのルートパラメータを含みます。例えば、ページ名が[id].jsの場合、params{ id: ... }のようになります。詳細は動的ルーティングドキュメントを参照してください。これは後で説明するgetStaticPathsと一緒に使用する必要があります。

以下は、CMSからブログ投稿リストを取得するためにgetStaticPropsを使用する例です:

pages/blog.js
// 任意のデータ取得ライブラリを使用できます
import fetch from 'node-fetch';
 
// postsはgetStaticProps()によってビルド時に設定されます
function Blog({ posts }) {
  return (
    <ul>
      {posts.map((post) => (
        <li>{post.title}</li>
      ))}
    </ul>
  );
}
 
// この関数はビルド時にNode.js環境で呼び出されます
// クライアントサイドでは呼び出されないので、
// 直接データベースクエリも可能です。「技術的詳細」セクションを参照
export async function getStaticProps() {
  // 外部APIエンドポイントを呼び出して投稿を取得
  const res = await fetch('https://.../posts');
  const posts = await res.json();
 
  // { props: posts }を返すことで、Blogコンポーネントは
  // ビルド時に`posts`をpropsとして受け取ります
  return {
    props: {
      posts,
    },
  };
}
 
export default Blog;

getStaticPropsはいつ使うべきか?

以下の場合にgetStaticPropsを使用するべきです:

  • ページをレンダリングするために必要なデータがユーザーリクエスト前にビルド時に利用可能
  • データがヘッドレスCMSから来る場合
  • データが公開キャッシュ可能(ユーザー固有でない)場合
  • ページがプリレンダリング(SEOのため)され、非常に高速である必要がある場合 - getStaticPropsはHTMLとJSONファイルを生成し、どちらもCDNでキャッシュ可能

getStaticPropsの詳細についてはデータ取得ドキュメントを参照してください。

getStaticPaths

ページが動的ルートを持ち、getStaticPropsを使用する場合、ビルド時にHTMLにレンダリングする必要があるパスのリストを定義する必要があります。

動的ルートを使用するページからgetStaticPathsというasync関数をエクスポートすると、Next.jsはgetStaticPathsで指定されたすべてのパスを静的にプリレンダリングします。

pages/posts/[id].js
export async function getStaticPaths() {
  return {
    paths: [
      { params: { ... } } // 「paths」セクションを参照
    ],
    fallback: true or false // 「fallback」セクションを参照
  };
}

pathsキー(必須)

pathsキーはどのパスがプリレンダリングされるかを決定します。例えば、pages/posts/[id].jsという動的ルートを使用するページがあるとします。このページからgetStaticPathsをエクスポートし、pathsに対して以下を返す場合:

return {
  paths: [
    { params: { id: 1 } },
    { params: { id: 2 } }
  ],
  fallback: ...
}

Next.jsはpages/posts/[id].jsのページコンポーネントを使用して、ビルド時にposts/1posts/2を静的に生成します。

paramsの値はページ名で使用されるパラメータと一致する必要があります:

  • ページ名がpages/posts/[postId]/[commentId]の場合、paramsにはpostIdcommentIdが含まれる必要があります
  • ページ名がpages/[...slug]のようなキャッチオールルートを使用する場合、paramsには配列であるslugが含まれる必要があります。例えば、この配列が['foo', 'bar']の場合、Next.jsは/foo/barでページを静的に生成します

fallbackキー(必須)

getStaticPathsが返すオブジェクトにはbooleanのfallbackキーが含まれている必要があります。

fallback: false

fallbackfalseの場合、getStaticPathsで返されないパスは404ページになります。これはすべてのパスがビルド時にわかっている場合に有用です。

以下はpages/posts/[id].jsというページごとに1つのブログ投稿をプリレンダリングする例です。ブログ投稿のリストはCMSから取得され、getStaticPathsで返されます。その後、各ページに対して、getStaticPropsを使用してCMSから投稿データを取得します。

pages/posts/[id].js
import fetch from 'node-fetch';
 
function Post({ post }) {
  // 投稿をレンダリング...
}
 
// この関数はビルド時に呼び出されます
export async function getStaticPaths() {
  // 外部APIエンドポイントを呼び出して投稿を取得
  const res = await fetch('https://.../posts');
  const posts = await res.json();
 
  // 投稿に基づいてプリレンダリングするパスを取得
  const paths = posts.map((post) => `/posts/${post.id}`);
 
  // ビルド時にこれらのパスのみをプリレンダリング
  // { fallback: false }は他のルートが404になることを意味します
  return { paths, fallback: false };
}
 
// これもビルド時に呼び出されます
export async function getStaticProps({ params }) {
  // paramsには投稿`id`が含まれます
  // ルートが/posts/1のような場合、params.idは1です
  const res = await fetch(`https://.../posts/${params.id}`);
  const post = await res.json();
 
  // props経由でページに投稿データを渡す
  return { props: { post } };
}
 
export default Post;

fallback: true

fallbacktrueの場合、getStaticPropsの動作が変わり、Next.jsは提供されたパスをビルド時にHTMLにレンダリングします。ビルド時に生成されなかったパスは、ユーザーがページをリクエストしたときにオンデマンドで生成されます。

これは、アプリケーションに静的に生成可能な多くのルートがあるが、ビルド時にページのサブセットのみを生成することでビルド時間の増加を避けたい場合に有用です。

ページの生成をトリガーするユーザーにはフォールバックHTMLが提供されます。これは一般的にローディング状態のページです。これは、静的なHTMLがCDNから提供されるため、ページがまだ生成されていなくても常に高速であることを保証するためです。

追加ページをオンデマンドで静的に生成する例:

pages/posts/[id].js
import { useRouter } from 'next/router';
import fetch from 'node-fetch';
 
function Post({ post }) {
  const router = useRouter();
 
  // ページがまだ生成されていない場合、
  // getStaticProps()の実行が終わるまでこれが表示されます
  if (router.isFallback) {
    return <div>Loading...</div>;
  }
 
  // 投稿をレンダリング...
}
 
// この関数はビルド時に呼び出されます
export async function getStaticPaths() {
  return {
    // `/posts/1`と`/posts/2`のみがビルド時に生成されます
    paths: [{ params: { id: 1 } }, { params: { id: 2 } }],
    // 追加ページを静的に生成可能に
    // 例: `/posts/3`
    fallback: true,
  };
}
 
// これもビルド時に呼び出されます
export async function getStaticProps({ params }) {
  // paramsには投稿`id`が含まれます
  // ルートが/posts/1のような場合、params.idは1です
  const res = await fetch(`https://.../posts/${params.id}`);
  const post = await res.json();
 
  // props経由でページに投稿データを渡す
  return { props: { post } };
}
 
export default Post;

getStaticPathsの詳細についてはデータ取得ドキュメントを参照してください。

getServerSideProps

ページからgetServerSidePropsというasync関数をエクスポートすると、Next.jsはこのページを各リクエストごとにサーバーサイドレンダリング (SSR) で描画します。

getServerSidePropsは常にサーバーサイドで実行され、コードは自動的にブラウザバンドルからツリーシェイクされます。これにより、ブラウザに送信されるコード量が削減されます。このため、サーバーとブラウザ環境間でデータ取得コードの実行に一貫性がない問題を気にする必要がなくなります。多くの場合、サーバーはデータソースへのより高速な接続を持っているため、パフォーマンスが向上します。また、データ取得ロジックの露出を減らすことでセキュリティも向上します。

これにより、fetch、REST、GraphQL、さらにはデータベースへの直接アクセスを含む、あらゆる非同期または同期データ取得技術を使用できます。

next/linkを使用してページ間を移動する場合、ブラウザでgetServerSidePropsを実行する代わりに、Next.jsはサーバーに対してfetchを実行し、getServerSidePropsの呼び出し結果を返します。

pages/index.js
export async function getServerSideProps(context) {
  return {
    props: {}, // ページコンポーネントにpropsとして渡されます
  };
}

contextパラメータは以下のキーを含むオブジェクトです:

以下は、getServerSidePropsを使用してリクエスト時にデータを取得し、それをレンダリングする例です:

pages/index.js
function Page({ data }) {
  // データをレンダリング...
}
 
// これは各リクエストで呼び出されます
export async function getServerSideProps() {
  // 外部APIからデータを取得
  const res = await fetch(`https://.../data`);
  const data = await res.json();
 
  // propsを介してページにデータを渡す
  return { props: { data } };
}
 
export default Page;

getServerSidePropsの詳細については、データ取得ドキュメントを参照してください。

プレビューモード

この投稿で前述したように、静的生成 (Static Generation) はページがヘッドレスCMSからデータを取得する場合に有用です。しかし、ヘッドレスCMSで下書きを書いていて、その下書きをすぐにページでプレビューしたい場合には理想的ではありません。出力が静的であるため、変更をプレビューするのが難しくなります。その静的ページを再生成する必要があるからです。

Next.jsのgetStaticPropsの導入により、特定の条件下でNext.jsのオンデマンドレンダリング機能を活用するなど、新しい可能性が開けました。

例えば、ヘッドレスCMSの下書きをプレビューする場合、静的レンダリングをバイパスし、公開済みのコンテンツではなく下書きコンテンツでページをオンデマンドでレンダリングしたいでしょう。この特定のケースでのみ、Next.jsに静的生成をバイパスさせたいはずです。

このニーズに対応するため、Next.jsの新しい組み込み機能を発表できることを嬉しく思います: プレビューモードです。

プレビューモードでは、ユーザーは静的に生成されたページをバイパスし、例えばCMSからの下書きページをオンデマンドでサーバーサイドレンダリング (SSR) できます。

ただし、特定のCMSシステムに限定されません。プレビューモードはgetStaticPropsgetServerSidePropsの両方と直接統合されているため、あらゆるタイプのデータ取得ソリューションで使用できます。

プレビューモードは、next startを使用する場合、またはVercel Edge Networkデプロイすることでシームレスに利用可能です。

プレビューモードを実際に試すには、https://next-preview.vercel.app/にアクセスしてください。

プレビューモードの詳細については、ドキュメントを参照してください。

CMSプロバイダーとの協業

getStaticPropsを使用すると、CMSシステムを含むあらゆるデータソースからデータを取得できます

私たちは、Next.jsとの統合に関する例やガイドを提供するため、CMSエコシステムの多くの主要プレイヤーと積極的に協力しています。

現在積極的に取り組んでいる例には以下があります:

あなたの会社がCMSエコシステムで活動している場合、ぜひ協力したいと思います!私たちのチームにメールまたはTwitterでご連絡ください。

グローバルスタイルシートのための組み込みSassサポート

Next.js 9.2では、より最適化された結果を提供するため、next-cssプラグインを置き換えるグローバルCSSスタイルシートの組み込みサポートが導入されました。

リリース直後、多くの企業がNext.jsに移行する際にSassベースの既存のデザインシステムを持っているため、Sassサポートの統合を求める声が増えました。

Next.jsプラグインの使用状況を調査したところ、約30%のNext.jsアプリケーションが現在next-sassを使用していることがわかりました。比較すると、44%がバニラCSS、6%がLessを使用しています。

さらに、next-sassにはnext-cssと同じ制約の欠如がありました。つまり、プロジェクトのすべてのファイルでSassファイルをインポートできましたが、このインポートされたSassファイルはアプリケーション全体でグローバルになります。

これらの統計とフィードバックを考慮した結果、Next.jsに組み込みのSassスタイルシートインポートサポートが追加されたことを嬉しく思います。

アプリケーションでグローバルSassインポートを使用するには、まずsassをインストールしてください:

Terminal
npm install sass

次に、pages/_app.js内でSassファイルをインポートします。

例えば、プロジェクトのルートにstyles.scssという名前のスタイルシートがある場合:

$primary-color: #333;
 
body {
  padding: 20px 20px 60px;
  margin: 0;
  color: $primary-color;
}

まだ存在しない場合はpages/_app.jsファイルを作成します。その後、styles.scssファイルをインポートします:

pages/_app.js
import '../styles.scss';
 
// このデフォルトエクスポートは新しい`pages/_app.js`ファイルで必要です。
export default function MyApp({ Component, pageProps }) {
  return <Component {...pageProps} />;
}

スタイルシートは本質的にグローバルであるため、カスタム<App>コンポーネントでインポートする必要があります。これは、グローバルスタイルのクラス名と順序の衝突を避けるために必要です。

開発時には、この方法でスタイルシートを表現すると、編集時にページ上のスタイルが自動的に更新されます。

本番環境では、すべてのSassとCSSファイルが自動的に単一のminified .cssファイルに連結されます。このCSSファイルは<link>タグを介してロードされ、Next.jsが生成するデフォルトのHTMLマークアップに自動的に注入されます。

この新機能は完全に後方互換性があります。@zeit/next-sassや他のCSS関連プラグインを使用している場合、競合を避けるためにこの機能は無効になります。

現在@zeit/next-sassを使用している場合は、next.config.jspackage.jsonからプラグインを削除し、アップグレード時に組み込みのSassサポートに移行することをお勧めします。

コンポーネントレベルのスタイルのための組み込みSass CSSモジュールサポート

Next.jsは現在、[name].module.scssファイル命名規則を使用して、SassファイルでのCSSモジュールをサポートしています。

以前Next.js 5+でnext-sassを使用して利用可能だったサポートとは異なり、グローバルSassとCSSモジュールが共存できるようになりました - next-sassでは、アプリケーション内のすべての.scssファイルをグローバルまたはローカルのいずれかとして処理する必要がありましたが、両方はできませんでした。

CSSモジュールは、一意のクラス名を自動的に作成することでSassをローカルスコープにします。これにより、異なるファイルで同じSassクラス名を使用しても衝突を心配する必要がなくなります。

この動作により、CSSモジュールはコンポーネントレベルのSassを含める理想的な方法となります。CSSモジュールファイルはアプリケーションのどこでもインポートできます

アプリケーションでSass CSSモジュールを使用するには、まずsassがインストールされていることを確認してください:

Terminal
npm install sass

次に、components/フォルダ内の再利用可能なButtonコンポーネントを考えてみましょう:

まず、以下の内容でcomponents/Button.module.scssを作成します:

/*
他の`.css`や`.module.css`ファイルとの衝突を心配する必要はありません!
*/
$color: white;
 
.error {
  color: $color;
  background-color: red;
}

次に、components/Button.jsを作成し、上記のCSSファイルをインポートして使用します:

components/Button.js
import styles from './Button.module.scss';
 
export function Button() {
  return (
    <button
      type="button"
      // インポートした`styles`オブジェクトのプロパティとして"error"クラスにアクセスする方法に注目
      className={styles.error}
    >
      Destroy
    </button>
  );
}

SassファイルのCSSモジュールはオプション機能であり、.module.scss拡張子を持つファイルに対してのみ有効になります。通常の<link>スタイルシートグローバルSassスタイルも引き続きサポートされます。

本番環境では、すべてのCSSモジュールファイルが自動的に多くのminifiedおよびコード分割された.cssファイルに連結されます。これらの.cssファイルはアプリケーション内のホットエグゼキューションパスを表し、アプリケーションが描画するためにページごとに最小限のCSSがロードされることを保証します。

上記と同様に、この新機能は完全に後方互換性があります。@zeit/next-sassや他のCSS関連プラグインを使用している場合、競合を避けるためにこの機能は無効になります。

現在@zeit/next-sassを使用している場合は、next.config.jspackage.jsonからプラグインを削除し、組み込みのSassサポートに移行することをお勧めします。

404の自動静的最適化

Next.js 9のリリースでは、ページにブロッキングデータ要件がない場合に自動静的最適化の概念が導入されました。Next.jsはビルド時に自動的にページを静的HTMLとして生成します。しかし、自動的に静的HTMLとしてレンダリングされないページが1つありました: 404ページです。404ページが自動的に静的にならなかった主な理由は、404を処理する/_errorページが404だけでなくエラーなども処理していたためです。

存在しないルートに対して404ページがレンダリングされるため、オンデマンドでページをレンダリングするとコストとサーバー負荷が増加する可能性があります。

私たちは2つの方法であなたを成功に導くことを目指しました:

  • デフォルトのNext.jsエクスペリエンスでは静的404ページが生成されます
  • 404ページをカスタマイズする場合でも、静的ページになることが保証されます

この機能は完全に後方互換性があるため、現在カスタムpages/_error.jsを持っている場合、pages/404.jsを追加するまで404ページに引き続き使用されます。

デフォルトの静的404ページ

アプリケーションにカスタムpages/_error.jsページがない場合、Next.jsは自動的に404ページを静的に生成し、404を提供する必要があるときにそれを使用します。これは自動的に行われ、変更は必要ありません。

pages/404.jsを使用したカスタム404ページ

デフォルトの404ページを上書きするには、pages/404.jsを作成できます。これもビルド時に自動的に静的に最適化されます。このページは、アプリケーションに存在する場合、404をレンダリングするためにpages/_error.jsの代わりに使用されます。

pages/404.js
export default () => <h1>This is the 404 page</h1>;

32kB以上小さいランタイム (15kB+ Gzip)

Next.jsはReact自体と同じブラウザをサポートしており、設定は不要です。これにはInternet Explorer 11 (IE11)とすべての主要なブラウザ(Edge、Firefox、Chrome、Safari、Operaなど)が含まれます。

この互換性の一環として、アプリケーションをIE11互換にコンパイルします: これにより、ES6+構文機能、Async/Await、オブジェクトのRest/Spreadプロパティなどを安全に使用できます - すべて設定不要で利用可能です。

このコンパイルプロセスの一部として、必要な機能ポリフィル(例: Array.fromSymbol)も透過的に注入されます。ただし、これらのポリフィルはウェブトラフィックの10%未満、ほとんどの場合IE11をサポートするためにのみ必要です。

Next.js 9.3以降、Next.jsはレガシーブラウザをサポートするために必要なポリフィルを自動的にロードし、これらのレガシーブラウザでのみポリフィルをロードします。

実際には、これはユーザーの90%以上にとって、_初回読み込み_サイズから32kB以上が削減されることを意味します。

これらのサイズ節約は、さらに多くのブラウザ機能に依存する大きなアプリケーションではさらに大きくなります。

この最適化は完全に自動的であり、この利点を活用するためにアプリケーションの変更は必要ありません!

コミュニティ

Next.jsの採用が継続的に成長しているのを見て非常に興奮しています:

  • 927名以上の独立したコントリビューターがいます。
  • GitHubでは、プロジェクトが46,600回以上スターされています。
  • examplesディレクトリには226以上の例があります。

Next.jsコミュニティには現在15,250人以上のメンバーがいます。コミュニティは現在GitHubディスカッションで見つけることができ、コミュニティが議論し質問する新しい場所です!参加してください!

私たちはコミュニティと、このリリースを形作るのに役立ったすべての外部フィードバックと貢献に感謝しています。

新しいデータ取得方法に関する重要なフィードバックを提供してくれたJeff Escalanteに特に感謝します。

このリリースに貢献してくれたすべての方々に大感謝です: @arcanis, @lgordey, @ijjk, @martpie, @jaywink, @fabianishere, @dijs, @TheRusskiy, @quinnturner, @timneutkens, @lfades, @vvo, @adithwip, @rafaelalmeidatk, @bmathews, @Spy-Seth, @EvgeniyKumachev, @chibicode, @piglovesyou, @HaNdTriX, @Timer, @janicklas-ralph, @devknoll, @prateekbh, @ethanryan, @MoOx, @rifaidev, @msweeneydev, @motiko, and @balazsorban44 for helping!