Next.jsバージョン13の変更点を時短で解説

blog-hero-imgv13で導入されたappフォルダやgenerateStaticParams。要点だけを手短にまとめて紹介します。

pen-icon2023.04.19rewrite-icon2023.05.18

Profile Pic

この記事の筆者:三好アキ(エンジニア)


ウェブデザイナーから『エンジニア』『プログラマー』へ成長したい人、独学で進んでいきたい人を応援しています。 HTMLとCSSの知識だけでアプリ開発を始められる入門書を多数執筆中📕📗👇


ウェブ制作の教本『はじめてつくるReactアプリ』など複数冊を執筆。



React、Next.js、TypeScriptなどのお役立ち情報や実践的コンテンツを、ビギナー向けにかみ砕いて配信中。登録はこちらから → 無料メルマガ登録

Next.js v13のAppフォルダ

Next.jsバージョン13ではAppフォルダが導入され、これによってルーティングやデータ取得の方法が大きく変わりました。

本記事ではその変更点をコードを見ながら紹介します。

よりくわしくAppフォルダを使った最新のNext.jsについて知りたい方は、先日リリースした下記書籍を参考にしてください。

はじめてつくるNext.jsサイト

【最新バージョンのNext.jsとAppフォルダで、時短でNext.jsを習得】

1759円0円

Amazonのページに移動する

なお本記事では全般的な「大きな」変更点について紹介しています。パス取得やリダイレクトのコード、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フォルダのあることです。

pic1.jpg

従来まではpagesフォルダ内にabout.jscontact.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というフォルダを作ります。

pic2.jpg

この中にファイルpage.js作ります。

pic3.jpg

そこに次のコードを打ってください。普通の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フォルダ内に作ります。

pic4.jpg

その中にpage.jsファイルを作りましょう。

pic5.jpg

次のコードを打ちます。

// 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を作ります。

pic6.jpg

そこに次のコードを打てば/blogページができます。

// app/blog/page.js

const Blog = () => {
    return (
        <h1>Blogページ</h1>
    )
}

export default Blog

blogフォルダの中に[slug]フォルダを作ってください。

pic7.jpg

その中にpage.jsファイルを作ります。

pic8.jpg

そこに次のコードを打ちます。

// app/blog/[slug]/page.js

const SingleBlog = () => {
    return (
        <h1>SingleBlogページ</h1>
    )
}

export default SingleBlog

保存します。URL欄のhttp://localhost:3001/blog//blog/以下にどの文字列をつなげても、いま作ったページが表示されるようになります。たとえばhttp://localhost:3001/blog/aabbcchttp://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.idprops.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を作ります。

pic9.jpg

次のコードを打ちます。

// 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フォルダ内ではgetStaticPathsgetStaticPropsが使えなくなりました。appフォルダ内で動的データをあつかう新しいコードの書き方の例を紹介します。

まず準備として、ダミーデータの入ったdataフォルダとそのデータ取得のためのutilsフォルダを拙著『はじめてつくるNext.jsサイト』の下記完成見本コードよりダウンロードし、appフォルダ内に配置してください。


完成見本コード: https://github.com/mod728/nextjs-book-portfolio-site

pic10.jpg

次にマークダウンファイルを扱うためのパッケージをインストールしましょう。

npm install raw-loader gray-matter

そしてnext.config.jsraw-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時間程度でストレスなく、かつ挫折することなく終えられるビギナーに優しい内容になっています。

nextbook