Next.js バージョン13ではこれまでのバージョンに比べて割とドラスティックな変更内容が多くなっています。
中でも app ディレクトリの採用とそれにまつわるページコンポーネントの仕組みは、ある程度その概念を理解して対応する必要があります。
とはいえ、待望のレイアウトが使えるようになったりと、私たちユーザーにもその進化が感じられる楽しいアップデートであると言えます。
この記事では、Next.js バージョン13へのアップデートのために必要な変更箇所を、簡単な新機能の解説を交えながら説明します。
目次
Next.js 13 の新機能のうち、対応が必要になるのは下記です。
- app ディレクトリ(ベータ版)
- layout ファイル
- page ファイル
- head ファイル
-
next/link
コンポーネント
以降はこれらの点について解説していきます。
全ての新機能については下記のオフィシャルブログをご覧ください。
公式のアップグレードガイドは下記をご覧ください。
下記ツイートのスレッドもわかりやすいので良かったら参考にしてください。
Node のバージョンが16.8以上であることを確認します。
16.8未満だった方はこの機会にバージョン18へのアップデートもご検討ください。
Next.js とその関連ライブラリをアップデートします。
お使いのパッケージソフトに合わせてコマンドを選んで実行してください。
npm install next@latest react@latest react-dom@latest eslint-config-next@latest
# or
yarn add next@latest react@latest react-dom@latest eslint-config-next@latest
# or
pnpm update next@latest react@latest react-dom@latest eslint-config-next@latest
next.config.js
で新機能の app ディレクトリを有効にします。
app ディレクトリによってこれまでよりも高速になりますが、現状はまだ実験的な機能であるため使用は自己責任でお願いします。
next.config.js
に experimental.appDir: true
を追加します。
const nextConfig = {
experimental: {
appDir: true,
},
};
これで準備は整いました。
以降は app ディレクトリと next/link
コンポーネントの移行方法について説明していきます。
Next.js 13ではページコンポーネントは、プロジェクトルートに新しく app
ディレクトリを作成してその下に配置します。
ページコンポーネントはそのまま移動すれば良いというわけではなく layout.tsx
, page.tsx
, head.tsx
の3ファイルに分割する必要があります。
また、動的ルーティングに関わるディレクトリ構成も変わっています。
app ディレクトリ有効後も、ディレクトリ構成をベースにした基本的なルーティングは変わりませんが、動的ルーティング部分のみ変更が必要です。
例えば pages/blog/index.tsx
は app ディレクトリ有効後は app/blog/
にファイルを配置します。
しかし例えば pages/blog/[slug].tsx
のような動的ルーティングは、 app/blog/[slug]/
のように動的ルーティングに対応したディレクトリを作成してその下にファイルを配置することになります。
APIルートは現状の pages/api
のまま変わりません。
そのほかの components
や styles
といったディレクトリ、ファイルはどこに配置しても構いません。
create next-app
初期化直後のプロジェクトを見てみると module.css
は app
以下の page.tsx
と同じ階層に配置しているようです。
Next.js 13 ではページの共通部分をレイアウトとして定義できるようになりました。
最初に root レイアウトとして app/layout.tsx
を作成します。
export default function DashboardLayout({
children, // will be a page or nested layout
}: {
children: React.ReactNode,
}) {
return (
<section>
{/* Include shared UI here e.g. a header or sidebar */}
<nav></nav>
{children}
</section>
);
}
子階層で個別のレイアウト使いたい場合はそのディレクトリに layout.tsx
を配置します。
同じ階層に layout.tsx
が存在しない場合は app/layout.tsx
が使用されます。
バージョン12以前は next/head
を使ってページコンポーネントなどに head
タグの内容を記述していましたが、バージョン13では next/head
の代わりに各階層に head.tsx
を配置します。
async function getPost(slug) {
const res = await fetch('...');
return res.json();
}
export default async function Head({ params }) {
const post = await getPost(params.slug);
return (
<>
<title>{post.title}</title>
</>
)
}
head
タグの内容が動的に変わる場合は、上記の例のようにデータ取得メソッドを head.tsx
ファイル内に定義することもできます。
1つ注意点として、バージョン12以前は viewport
に関する記述が自動的に入っていましたが、バージョン13では自動で入らなくなったので自前で viewport を定義する必要があります。
<meta name="viewport" content="width=device-width,initial-scale=1.0" />
各 head.tsx
の内容が繰り返しになってしまう場合には、コンポーネントにまとめて定義しておいて呼び出す方法が便利です。
const DocumentHead = ({ title = '', description = '' }) => {
return (
<>
<title>{title}</title>
<meta name="viewport" content="width=device-width,initial-scale=1.0" />
<meta
name="description"
content={description ? description : 'default description...'}
/>
</>
)
}
こうしておけば head.tsx
ではコンポーネントを呼び出すだけになりコードの見通しが良くなります。
import DocumentHead from '../../components/document-head'
const BlogHead = () => (
<DocumentHead title="Blog" />
)
export default BlogHead
Next.js 13からはサーバーコンポーネント、クライアントコンポーネントという概念が追加されました。
page.tsx
に限らず app
ディレクトリ以下の .tsx
ファイルは全てサーバーコンポーネントかクライアントコンポーネントかのどちらかとして扱う必要があり、デフォルトではサーバーコンポーネントとして扱われます。
基本的にはサーバーコンポーネントを用い、必要に応じて部分的にクライアントコンポーネントを使用するといったイメージです。
それではまず、移行前のページコンポーネントを元に page.tsx
を作成します。
getStaticProps()
メソッドは無くなったので、JSXを返す1つのメソッドとして定義します。
データ取得などは head.tsx
と同様に別メソッドを定義して呼び出します。
その際は必要に応じて async function
とするのを忘れないようにしましょう。
export default function Page({ params, searchParams }: {
params: { slug: string },
searchParams: { id: string },
}) {
return (
<>
<p>{params.slug}</p>
<p>{searchParams.id}</p>
</>
);
}
page.tsx
のうち、下記に該当する部分をクライアントコンポーネントにするため別ファイルに分割します。
-
onClick()
やonChange()
などを使いインタラクティブな処理を行っている -
useState()
やuseEffect()
などのフックを用いた処理を行っている -
window
などブラウザでのみ使用可能な API を用いている - 上記3つのいずれかに当てはまるカスタムコンポーネントを用いている
- React クラスコンポーネントを使用している
詳細は下記ドキュメントをご覧ください。
コンポーネントファイルをクライアントコンポーネントとして扱うにはファイルの先頭に 'use client'
を追加します。
'use client'
import React, { useState, useEffect } from 'react'
// ...
これでクライアントコンポーネントとして扱えるようになりました。
クライアントコンポーネントの定義はファイル単位なので、どのように分割するかはよく考える必要があります。
useRouter
フックは next/router
からインポートしていましたが、Next.js 13 では next/navigation
に変更になりました。
同時に router.events
は現状サポートされていないので、もし使用している場合は置き換える必要があります。
現在のURLパスを得る useRouter
フックの asPath
は usePathname()
を使って置き換えることができます。
詳細は下記の公式リファレンスをご覧ください。
参考までに私が行った置き換え差分も置いておきます。
blog/[slug].tsx
のような動的ルーティングを用いたページコンポーネントでは、動的なパラメータが取りうる値を getStaticPaths()
メソッドで定義しておくことができました。
export function getStaticPaths() {
return {
paths: ['/blog/hello-world', '/blog/my-first-post'],
}
}
app ディレクトリでは代わりに generateStaticParams()
を使います。
ただし、返すべき値の内容と構造が変わっているので注意してください。
export async function generateStaticParams() {
return [{ slug: 'hello-world' }, { slug: 'my-first-post' }]
}
未定義の動的パラメータでアクセスした際に404エラーとしたい場合は page.tsx
に export const dynamicParams = false
を定義します。
が、現状こちらは不具合がありそうなことに気づいたので下記で報告しています。
ISRを使用するには、以前は getStaticProps()
で { revalidate: 60 }
のように返していました。
Next.js 13 の app ディレクトリでは page.tsx
もしくは layout.tsx
で export const revalidate = 60
のように定義します。
時間以外の設定の詳細は下記公式リファレンスをご覧ください。
以上で page.tsx
の移行に必要な変更が完了しました。
Next.js 13 では next/link
の Link
コンポーネントが変更になりました。
以前のバージョンでは下記のように記述していました。
<Link href="/blog/[slug]" as={`/blog/${slug}`} passHref>
<a>{postTitle}</a>
</Link>
Next.js 13 では Link
コンポーネントの子要素の a
タグが不要になりました。
また、 Link
コンポーネントの as
や passPref
といったプロパティも廃止となりました。
上記の Link
コンポーネントは13では下記のようになります。
<Link href={`/blog/${slug}`}>
{postTitle}
</Link>
その他の詳細は下記の公式リファレンスをご覧ください。
最後に、私が開発しているソフトウェア easy-notion-blog を Next.js 13 にアップデートした際の全差分を置いておきます。
参考になれば幸いです。
以上、長くなりましたが Next.js バージョン13へのアップデート方法を解説しました。
Next.js 13 への移行は大変ですが、それはそれだけ挑戦的でワクワクさせてくれる機能が多いということを意味します。
ぜひ Next.js 13 へアップデートして新しい機能を楽しんでくださいね。
コメントを送る
コメントはブログオーナーのみ閲覧できます