このドキュメントはNext.jsのバージョン9.3以降が対象となります。旧バージョンのNext.jsを使用している場合は、以前のドキュメントを参照してください。
Pagesのドキュメントにおいて、Next.js には 2 種類のプリレンダリングがあることを説明しました。静的生成とサーバーサイドレンダリングです。このページでは、それぞれの場合でのデータ取得戦略について掘り下げていきます。 まず最初にPagesのドキュメントを読むことをお勧めします。
プリレンダリングのデータ取得に使える 3 つの特徴的な Next.js の関数についてお話します:
getStaticProps
(静的生成): ビルド時にデータ取得するgetStaticPaths
(静的生成): データに基づきプリレンダリングする動的ルートを特定するgetServerSideProps
(サーバーサイドレンダリング): リクエストごとにデータを取得するまた、クライアント側でのデータ取得について簡潔にお話します。
getStaticProps
(静的生成)ページから getStaticProps
という async
関数をエクスポートすると、Next.js はビルド時に getStaticProps
から返される props を使ってプリレンダリングします。
export async function getStaticProps(context) {
return {
props: {} // ページコンポーネントにpropsとして渡されます。
};
}
context
パラメータは次のキーを含むオブジェクトです:
params
はページが動的ルートを利用するためのルートパラメータを持ちます。たとえば、ページ名が [id].js
である時、params
は { id: ...}
のように見えます。詳細は 動的ルーティングのドキュメントをご覧ください。後に説明する getStaticPaths
と一緒に使う必要があります。preview
が true
になり、そうでない場合は false
になります。プレビューモードのドキュメントをご覧ください。previewData
は、setPreviewData
によって設定されたプレビューデータを含みます。プレビューモードのドキュメント
をご覧ください。getStaticProps
を使って CMS(コンテンツマネジメントシステム)からブログ記事の一覧を取得する例です。この例はPagesのドキュメントにもあります。
// posts はビルド時に getStaticProps() によって生成されます。
function Blog({ posts }) {
return (
<ul>
{posts.map(post => (
<li>{post.title}</li>
))}
</ul>
);
}
// この関数はサーバー側でのビルド時に呼び出されます。
// クライアント側では呼び出されないため、データベースクエリを直接実行することも可能です。
// 「技術詳細」のセクションをご覧ください。
export async function getStaticProps() {
// 外部のAPIエンドポイントを呼び出してpostsを取得します。
// 任意のデータ取得ライブラリを使用できます。
const res = await fetch('https://.../posts');
const posts = await res.json();
// { props: posts } を返すことで、Blog コンポーネントは
// ビルド時に`posts`を prop として受け取ります。
return {
props: {
posts
}
};
}
export default Blog;
getStaticProps
をいつ使うべきか?getStaticProps
を使うのはこんな時。
getStaticProps
は HTML と JSON ファイルを生成し、どちらもパフォーマンスのために CDN でキャッシュされる。GetStaticProps
を使うTypeScript では、GetStaticProps
型を next
から利用できます。
import { GetStaticProps } from 'next';
export const getStaticProps: GetStaticProps = async context => {
// ...
};
process.cwd()
を使うファイルは getStaticProps
でファイルシステムから直接読み込みできます。
そのためにはファイルのフルパスが必要です。
Next.js はコードを別のディレクトリにコンパイルするため、__dirname
はページディレクトリとは異なり、パスとしては使えません。
代わりに process.cwd()
を使って Next.js が実行されたディレクトリを取得できます。
import fs from 'fs';
import path from 'path';
// posts はビルド時に getStaticProps() によって生成されます。
function Blog({ posts }) {
return (
<ul>
{posts.map(post => (
<li>
<h3>{post.filename}</h3>
<p>{post.content}</p>
</li>
))}
</ul>
);
}
// この関数はサーバー側のビルド時に呼び出されます。
// クライアント側では呼び出されないので、直接データベースクエリを実行できます。
// 「技術詳細」セクションをご覧ください。
export async function getStaticProps() {
const postsDirectory = path.join(process.cwd(), 'posts');
const filenames = fs.readdirSync(postsDirectory);
const posts = filenames.map(filename => {
const filePath = path.join(postsDirectory, filename);
const fileContents = fs.readFileSync(filePath, 'utf8');
// 通常はコンテンツをパースもしくは変換するでしょう。
// たとえば、ここではマークダウンをHTMLに変換できます。
return {
filename,
content: fileContents
};
});
// { props: posts } を返すことで、Blogコンポーネントはビルド時に
// `posts`をpropとして受け取ります。
return {
props: {
posts
}
};
}
export default Blog;
getStaticProps
はビルド時に実行されるため、リクエスト時でのみ利用可能なクエリパラメータや、静的 HTML を生成した時の HTTP ヘッダーのようなデータを受け取りません。
getStaticProps
はサーバー側でのみ実行されることに注意してください。クライアント側では決して実行されません。ブラウザ用の JS バンドルにも含まれません。つまり、直接データベースクエリのようなコードを書いてもブラウザに送信されることはないということです。API ルートを getStaticProps
から取得するのではなく、代わりに getStaticProps
で直接サーバー側のコードを書くことができます。
getStaticProps
を持つページがビルド時にプリレンダリングされると、ページの HTML ファイルだけでなく Next.js は getStaticProps
の結果を持つ JSON ファイルを生成します。
この JSON ファイルは、next/link
(ドキュメント)もしくは next/router
(ドキュメント)経由のクライアント側のルーティングで使われます。getStaticProps
でプリレンダリングされたページに遷移すると、Next.js はこの JSON ファイル(ビルド時に事前処理)を取得してページコンポーネントの props として使います。つまり、クライアント側のページ遷移は getStaticProps
を呼び出さず、エクスポートされた JSON だけが使われます。
getStaticProps
は ページ からのみエクスポートされます。ページ以外のファイルからはエクスポートできません。
この制約の理由の 1 つは、React がページレンダリングの前に必要なデータを全て持つ必要があるという点です。
また、export async function getStaticProps() {}
を使用しなければなりません。getStaticProps
をページコンポーネントのプロパティとして追加しても機能しません。
開発中(next dev
)は、getStaticProps
はリクエストごとに呼び出されます。
一時的に静的生成を迂回してビルド時ではなくリクエスト時にレンダリングしたい時もあるでしょう。たとえば、ヘッドレス CMS を使って公開前に下書きをプレビューするような時です。
このユースケースは Next.js のプレビューモードという機能でサポートされています。詳細はプレビューモードのドキュメントをご覧ください。
getStaticPaths
(静的生成)ページが動的ルート(ドキュメント)を持ち、getStaticProps
を使用する場合、ビルド時に HTML をレンダリングするためのパス一覧を定義する必要があります。
動的ルートを使ったページから getStaticPaths
という async
関数をエクスポートすると Next.js は getStaticPaths
で指定された全パスを静的にプリレンダリングします。
export async function getStaticPaths() {
return {
paths: [
{ params: { ... } } // 下記の「パス」セクションをご覧ください。
],
fallback: true or false // 下記の「フォールバック」セクションをご覧ください。
};
}
paths
キー(必須)paths
キーはどのパスがプリレンダリングされるかを決定します。たとえば、動的ルートを使った pages/posts/[id].js
というページがあります。このページから getStaticPaths
をエクスポートして paths
に次のような値を返すとしましょう。
return {
paths: [
{ params: { id: '1' } },
{ params: { id: '2' } }
],
fallback: ...
}
Next.js はビルド時に pages/posts/[id].js
でページコンポーネントを使って 静的に
posts/1と
posts/2`を生成します。
各 params
の値はページ名で使われたパラメータと一致しなければならないことに注意してください。
pages/posts/[postId]/[commentId]
であれば、params
は postId
と commentId
を含まなければなりません。
-pages/[...slug] のように
ページ名が catch-all ルートを使用していれば、params
は slug
という配列を含まなければなりません。たとえば、この配列が ['foo', 'bar']
であれば、 Next.js は静的に /foo/bar
というページを生成します。fallback
キー(必須)getStaticPaths
で返されるオブジェクトはブール値の fallback
キーを含まなければなりません。
fallback: false
fallback
が false
であれば、getStaticPaths
で返されないパスは全て404 ページとなります。プリレンダリングの必要なパスが少ない時に使えます。つまり、ビルド時に全てのパスが静的に生成されます。新しいページがあまり追加されないような時にも役立ちます。データソースに項目を増やして新しいページをレンダリングしたい場合には、再度ビルドする必要があります。
こちらは pages/posts/[id].js
というページごとに 1 件のブログ記事をプリレンダリングする例です。ブログ記事の一覧は CMS から取得され、getStaticPaths
で返されます。各ページでは、getStaticProps
を使って CMS から記事データを取得します。この例はページのドキュメントにもあります。
// pages/posts/[id].js
function Post({ post }) {
// 記事をレンダリングします...
}
// この関数はビルド時に呼び出されます。
export async function getStaticPaths() {
// 外部APIエンドポイントを呼び出して記事を取得します。
const res = await fetch('https://.../posts');
const posts = await res.json();
// 記事に基づいてプリレンダリングしたいパスを取得します
const paths = posts.map(post => ({
params: { id: 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
fallback
が true
の時、getStaticProps
の振る舞いは変わります。
getStaticPaths
が返すパスはビルド時に HTML でレンダリングされます。getStaticProps
の実行を含みます。「フォールバック」版ページの特徴は下記のとおりです。
route.isFallback
は true
となります。isFallback
を使った例を示します。
// pages/posts/[id].js
import { useRouter } from 'next/router';
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;
fallback: true
がいつ役立つか?fallback: true
は、アプリケーションがデータに依存する多数の静的ページ(巨大な E コマースサイトなど)を持つ時に役立ちます。全商品のページをプリレンダリングしたくても、ビルドに膨大な時間がかかってしまうような時です。
代わりに、ページの小さなサブセットを静的に生成し、残りは fallback: true
にできます。未生成のページがリクエストされると、ユーザーにはローディングインジケーターが表示されます。その直後に、getStaticProps
が完了して、ページはリクエストされたデータでレンダリングされます。それ以降は、同じページがリクエストされると静的にプリレンダリングされたページが表示されます。
これにより、ユーザーは高速なビルドと静的生成の利点を保ったまま、常に高速な体験を得ることができます。
getStaticPaths
をいつ使うべきか?動的ルートを使ったページを静的にプリレンダリングするときに getStaticPaths
を使うと良いでしょう。
GetStaticPaths
を使うTypeScript では、next
から GetStaticPaths
型を利用できます。
import { GetStaticPaths } from 'next';
export const getStaticPaths: GetStaticPaths = async () => {
// ...
};
getStaticProps
と一緒に使う動的ルートパラメータを持つページで getStaticProps
を使う時は getStaticPaths
を使わなければなりません。
getStaticPaths
と getServerSideProps
は併用できません。
getStaticPaths
はサーバー側のビルド時のみ実行されます。
getStaticPaths
はページからのみエクスポートできます。ページ以外のファイルからはエクスポートできません。
また、export async function getStaticPaths() {}
を使わなければなりません。これは getStaticPaths
をページコンポーネントのプロパティとして追加しても動作しません。
開発中(next dev
)は、getStaticPaths
はリクエストごとに呼び出されます。
getServerSideProps
(サーバーサイドレンダリング)getServerSideProps
という async
関数をエクスポートすると Next.js はリクエストごとに getServerSideProps
から返されるデータでプリレンダリングします。
export async function getServerSideProps(context) {
return {
props: {} // ページコンポーネントにpropsとして渡されます。
};
}
context
パラメータは以下のキーを含むオブジェクトです。
params
: ページが動的ルートを使えば、params
はルートパラメータを含みます。ページ名が [id].js
であれば、params
は { id: ... }
のようになります。詳細は動的ルーティングのドキュメントをご覧ください。
req
: HTTPインカミングメッセージオブジェクト。
res
: HTTPレスポンスオブジェクト。
query
: クエリストリング。
preview
: preview
はページがプレビューモードであれば true
、そうでなければ false
になります。プレビューモードのドキュメントをご覧ください。
previewData
: プレビューデータは setPreviewData
によって設定されます。プレビューモードのドキュメントをご覧ください。
getServerSideProps
を使ってリクエスト時にデータを取得してプリレンダリングする例を示します。この例は ページのドキュメントにもあります。
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
はいつ使うべきか?getServerSideProps
は、リクエスト時にデータを取得するページのプリレンダリングが必要な時のみ使うべきです。最初のバイトの所要時間(TTFB)は getStaticProps
よりも遅くなります。サーバーはリクエストごとに演算しなければならず、その結果は追加設定なしで CDN にキャッシュされないためです。
データをプリレンダリングする必要がなければ、クライアント側でのデータ取得を検討すべきです。詳細はこちら。
GetServerSideProps
を使うTypeScript では、GetServerSideProps
型を next
から利用できます。
import { GetServerSideProps } from 'next';
export const getServerSideProps: GetServerSideProps = async context => {
// ...
};
getServerSideProps
はサーバー側でのみ実行され、ブラウザでは決して実行されません。ページが getServerSideProps
を使うと次のような挙動になります。
getServerSideProps
はリクエスト時に実行され、返された props でプリレンダリングされます。next/link
(ドキュメント) や next/router
(ドキュメント) でのクライアント側のページ遷移をリクエストすると Next.js は API リクエストをサーバーに送信します。続けて getServerSideProps
を実行します。この挙動は全て自動的に Next.js によって処理されるため、getServerSideProps
を定義しさえすれば他にやることはありません。getServerSideProps
はページからのみエクスポートされます。ページ以外のファイルからはエクスポートされません。
また、export async function getStaticPaths() {}
を使わなければなりません。これは getServerSideProps
をページコンポーネントのプロパティとして追加しても動作しません。
ページが頻繁に更新されるデータを持ち、データをプリレンダリングする必要がないようであれば、クライアント側でデータ取得もできます。ユーザー固有のデータなどが該当します。次のように機能します。
このアプローチは、ユーザーダッシュボードのようなページに有効です。ダッシュボードはプライベートかつユーザー固有のページであり、SEO も不要なのでページをプリレンダリングする必要もないためです。データは頻繁に更新され、リクエスト時のデータ取得を必要とします。
Next.js の開発チームはデータ取得用の SWR という React フックを作成しました。クライアント側でデータ取得する際にはその利用を強くお勧めします。キャッシュ、再バリデーション、フォーカスの追跡、一定間隔での再取得などを処理できます。次のように使用します。
import useSWR from 'swr';
function Profile() {
const { data, error } = useSWR('/api/user', fetch);
if (error) return <div>failed to load</div>;
if (!data) return <div>loading...</div>;
return <div>hello {data.name}!</div>;
}
次のセクションを読むことをお勧めします。