Next.jsバージョン13の変更点を時短で解説
2023.04.192023.05.18
この記事は約3分で読めます
この記事の筆者:三好アキ
🔹 専門用語なしでプログラミングを教えるメソッドに定評があり、1200人以上のビギナーを、最新のフロントエンド開発入門に成功させる。
🔹 Amazonベストセラー1位を複数回獲得している『はじめてつくるReactアプリ with TypeScript』著者。
Amazon著者ページはこちら → amazon.co.jp/stores/author/B099Z51QF2
React、Next.js、TypeScriptなどのお役立ち情報や実践的コンテンツを、ビギナー向けにかみ砕いて無料配信中。登録はこちらから → 無料メルマガ登録
Next.js v13のAppフォルダ
Next.jsバージョン13ではAppフォルダが導入され、これによってルーティングやデータ取得の方法が大きく変わりました。
本記事ではその変更点をコードを見ながら紹介します。
よりくわしくAppフォルダを使った最新のNext.jsについて知りたい方は、先日リリースした下記書籍を参考にしてください。
はじめてつくるNext.jsサイト
【最新バージョンのNext.jsとAppフォルダで、時短でNext.jsを習得。作りながら学ぶ実践形式】
1848円 → 0円
なお本記事では全般的な「大きな」変更点について紹介しています。パス取得やリダイレクトのコード、apiフォルダといった個別的な「小さな」変更点は次の記事を参考にしてください。
v13のインストール
最新版のNext.jsのインストールから始めましょう。ターミナルを開いて任意のフォルダへ移動し、次のコマンドでNext.jsをインストールしてください。
npx create-next-app@latest nextjs13-app-test
最後の@latest
によって、その時点での最新版がインストールされます。フォルダ名はここではnextjs13-app-test
としています。
インストール開始前に次のような質問が出ます(2023年5月現在)。
? Would you like to use TypeScript with this project? … No / Yes
? Would you like to use ESLint with this project? … No / Yes
? Would you like to use Tailwind CSS with this project? … No / Yes
? Would you like to use `src/` directory with this project? … No / Yes
? Use App Router (recommended)? › No / Yes
? Would you like to customize the default import alias? › No / Yes
「Yes」か「No」を矢印キーで選択して「Enter」で決定します。TypeScript、ESLint、Tailwind CSS、src
directory利用の質問には「No」を選択してください。そして「Use App Router (recommended)」の質問には「Yes」を選ぶことで、v13の新機能app
フォルダが利用できるようになります。最後の「customize the default import alias」は今回重要ではないので「No」を押して進んでください。
インストールが完了したらVSコードで開きましょう。package.json
を見ると、"next": "13.x.x"
とあり、Next.jsのversion 13がインストールされているのがわかります(執筆時点では"next": "13.3.0"
)。
appフォルダ
v13ではフォルダ構成の方法に根本的な変更が加えられました。なお現時点(2023年4月)ではこの機能はまだ「experimental(実験段階)」ですが、今後はこれがデフォルトになっていくと考えられます。
(*5月4日にリリースされたNext.js v13.4よりstable(安定版)となりました)
まずnext.config.js
を見てください。
// next.config.js
/** @type {import('next').NextConfig} */
const nextConfig = {
experimental: {
appDir: true,
},
}
module.exports = nextConfig
experimental
という文字が見え、appDir
つまり「app Directory(appフォルダ)」機能が追加されているのがわかります。
次にフォルダ構成を見ると、最初に気がつくのはapp
フォルダのあることです。
従来まではpages
フォルダ内にabout.js
、contact.js
といったような各ページを作ってきましたが、app
フォルダでは各ページ毎にひとつフォルダを作り、その中にpage.js
というファイルを作る構成となります。
表にすると以下のようになりますが、実際に試した方がわかりやすいのでコードを見ながら解説していきます。
URL | 従来 | v13 |
---|---|---|
/ | /pages/index.js | /app/page.js |
/about | /pages/about.js | /app/about/page.js |
/contact | /pages/contact.js | /app/contact/page.js |
まずapp/page.js
を見てください。これは従来のpages/index.js
にあたります。
最初に/about
ページを作ってみましょう。app
フォルダ内にabout
というフォルダを作ります。
この中にファイルpage.js
作ります。
そこに次のコードを打ってください。普通のReactのコードです。
// app/about/page.js
const About = () => {
return (
<h1>Aboutページ</h1>
)
}
export default About
保存し、Next.jsを次のコマンドで起動しましょう。
npm run dev
http://localhost:3000/about
にアクセスするとAboutページが開きます(CSSスタイルとしてglobals.css
が当たっています)。
同じ要領でコンタクトページを作ってみましょう。まずcontact
フォルダをapp
フォルダ内に作ります。
その中にpage.js
ファイルを作りましょう。
次のコードを打ちます。
// app/contact/page.js
const Contact = () => {
return (
<h1>Contactページ</h1>
)
}
export default Contact
保存して、http://localhost:3000/contact
にアクセスするとContactページができているのがわかります。
ここまでで、従来のようにファイル名ではなく、フォルダ名がページのURLと対応しているのがわかりました。
従来のNext.jsの動的ページで使った特殊な名前を持つファイル、[slug].js
の作り方も発想としては同じです。[slug]
という名前をファイルではなくフォルダに使います。実際に確認してみましょう。
まずblog
フォルダを作り、その中にpage.js
を作ります。
そこに次のコードを打てば/blog
ページができます。
// app/blog/page.js
const Blog = () => {
return (
<h1>Blogページ</h1>
)
}
export default Blog
blog
フォルダの中に[slug]
フォルダを作ってください。
その中にpage.js
ファイルを作ります。
そこに次のコードを打ちます。
// app/blog/[slug]/page.js
const SingleBlog = () => {
return (
<h1>SingleBlogページ</h1>
)
}
export default SingleBlog
保存します。URL欄のhttp://localhost:3001/blog/
の/blog/
以下にどの文字列をつなげても、いま作ったページが表示されるようになります。たとえばhttp://localhost:3001/blog/aabbcc
やhttp://localhost:3001/blog/xxyyzz
などです。
なおこのページでURLのパラメータを取得するには次のようにします。
// app/blog/[slug]/page.js
const SingleBlog = (props) => { // 追加
console.log(props.params.slug) // 追加
return (
<h1>SingleBlogページ</h1>
)
}
export default SingleBlog
props
に含まれたparams
内部のslug
で、URLのパラメーターを取得できます。なおここでslug
となっているのはフォルダ名が[slug]
であるからで、もしフォルダ名が[id]
や[abc]
の場合、それぞれprops.params.id
、props.params.abc
となります。
Layoutコンポーネント
従来までサイト全体に適用したいスタイルなどは_app.js
に書き加えていましたが、v13ではapp
フォルダ内にlayout.js
というファイルを作ると、それがサイト全体に適用されます。
現在app
フォルダを見ると、すでにlayout.js
があり、これがサイト全体で適用されています。本当に適用されているのか確認したいので、次のように余分なタグを書き加えてみましょう。
// app/layout.js
import './globals.css'
export const metadata = {
title: 'Create Next App',
description: 'Generated by create next app',
}
export default function RootLayout({ children }) {
return (
<html lang="en">
<body><h1>テスト</h1>{children}</body> // 追加
</html>
)
}
保存して/about
、/contact
などにアクセスすると、「テスト」という文字が表示され、たしかにlayout.js
ファイルが全ページに適用されているのがわかります。
しかし一部のページでは別のレイアウトを適用したいというケースもあります。その場合はどうすればいいのでしょうか。
実はページ毎に作ったフォルダの中にlayout.js
を作ると、それがフォルダ内のすべてのページに適用されます。試してみましょう。
blog
フォルダ内にlayout.js
を作ります。
次のコードを打ちます。
// app/blog/layout.js
export default function BlogLayout({ children }) {
return (
<body>
<div>
<h1>ブログページレイアウト</h1>{children}
</div>
</body>
)
}
保存してhttp://localhost:3000/blog
を開くと、app/layout.js
のレイアウトではなく、いま作ったapp/blog/layout.js
が適用されているのがわかります
http://localhost:3000/blog/xyz
を開いてもそれが適用されているので、/blog/
以下のページにはすべてblog
フォルダ内のlayout.js
が適用されているのがわかります。
getStaticPathsとgetStaticPropsの変更点
app
フォルダにはReact Server Componentsという仕組みが使われているため、app
フォルダ内ではgetStaticPaths
とgetStaticProps
が使えなくなりました。app
フォルダ内で動的データをあつかう新しいコードの書き方の例を紹介します。
まず準備として、ダミーデータの入ったdata
フォルダとそのデータ取得のためのutils
フォルダを拙著『はじめてつくるNext.jsサイト』の下記完成見本コードよりダウンロードし、app
フォルダ内に配置してください。
完成見本コード: https://github.com/mod728/nextjs-book-portfolio-site
次にマークダウンファイルを扱うためのパッケージをインストールしましょう。
npm install raw-loader gray-matter
そしてnext.config.js
にraw-loader
を使うコードを書き加えます。
// next.config.js
/** @type {import('next').NextConfig} */
const nextConfig = {
experimental: {
appDir: true,
},
// ⬇追加
webpack: function (config) {
config.module.rules.push({
test: /\.md$/,
use: "raw-loader",
})
return config
},
// ⬆追加
}
module.exports = nextConfig
以上で準備が完了です。
URLの生成・登録に使われるgetStaticPaths
の新しいバージョンとして導入されたのがgenerateStaticParams
です。次のように書きます。
// app/blog/[slug]/page.js
import { getAllBlogs } from "../../utils/mdQueries"
const SingleBlog = () => {
return (
<h1>SingleBlogページ</h1>
)
}
export default SingleBlog
export async function generateStaticParams() {
const { orderedBlogs } = await getAllBlogs()
const paths = orderedBlogs.map((orderedBlog) => `/${orderedBlog.slug}`)
return paths
}
getStaticPaths
とともに、ページで表示するデータ取得のために使っていたのがgetStaticProps
でした。
app
フォルダ内ではgetStaticProps
は廃止され、データに直接アクセスする方法(下記例)や、JavaScriptのfetch()
でデータ取得ができるようになっています。コード例として次のようになります。なお非同期処理のasync
を書き忘れないようにしましょう。
// app/blog/[slug]/page.js
import { getAllBlogs, getSingleBlog } from "../../utils/mdQueries"
const SingleBlog = async(props) => {
const { singleDocument } = await getSingleBlog(props)
console.log(singleDocument)
return (
<h1>SingleBlogページ</h1>
)
}
export default SingleBlog
export async function generateStaticParams() {
const { orderedBlogs } = await getAllBlogs()
const paths = orderedBlogs.map((orderedBlog) => `/${orderedBlog.slug}`)
return paths
}
さらにくわしく知りたい方はビギナー向けの下記書籍を参考にしてください。
2時間程度でストレスなく、かつ挫折することなく終えられるビギナーに優しい内容になっています。
メルマガ配信中
(from 三好アキ/エンジニア)
React、Next.js、TypeScriptなど最新のウェブ開発のお役立ち情報を、ビギナー向けにかみ砕いて無料配信中。
(*配信はいつでも停止できます)