はじめに
この記事では、Next.js の基本的なインストール手順から最低限必要な機能(レイアウト、ページ、CSS、データフェッチ、ルーティング)をざっくりと解説します。これさえできれば簡単なアプリを作ることができます。
実際、ここに書いてある知識だけで以下のアプリや個人ブログを作りました。
※ 私は Next.js を始めて 2 か月くらい経ちます。まだ初心者なので間違っているところがあるかもしれません。
- 環境
$ node --version
v20.15.0
$ npm --version
10.7.0
インストール
npx create-next-app@latest
対話形式でプロジェクトの名前や様々なオプションの使用を選択することができます。迷ったときはデフォルト(何も押さずエンター押下)で問題ありません。
$ npx create-next-app@latest
Need to install the following packages:
create-next-app@15.1.3
Ok to proceed? (y) y
対話形式で入力したプロジェクト名のディレクトリが作成されるのでそこに移動し、サーバ起動を起動します。
cd next-todo-app
npm run dev
layout.tsx と page.tsx
レイアウト
レイアウトは複数のページ間で共有される UI です。ナビゲーション(ルーティングを行う処理のこと)において、レイアウトは状態を保持し、再レンダリングされません。
ルートレイアウト
ルートレイアウトは app ディレクトリのルートに定義されている laytout.tsx のことを指します。ルートレイアウトは必須であり、html タグと body タグを含んでいる必要があります。
app/
├── layout.tsx
├── page.module.css
└── page.tsx
export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
return (
<html>
<body>{children}</body>
</html>
);
}
ページ
ページとは特定のルートでレンダリングされるUIです。次のようにディレクトリ構成をルートに反映させる方法をファイルシステムルーティングと呼びます。特定のディレクトリパスに page.tsx を配置し、そのルートに対応するページ(UI)をレンダリングします。
- 例
ファイルパス <---> ルート(URLパス)
app/page.tsx <---> https://example.com/
app/about/page.tsx <---> https://example.com/about/
ページを作成するには、appディレクトリ内に page.tsx を追加し、Reactコンポーネントをデフォルトエクスポートします。例えばインデックスページ(/)を作成するには次のようにします:
import styles from "./page.module.css";
export default function Home() {
return (
<div className={styles.page}>
<main className={styles.main}>
<h1>Hello, Next.js!</h1>
</main>
</div>
);
}
CSS
CSS モジュール
CSS モジュールは、ユニークなクラス名を生成することによって CSS をローカルスコープ化します。これにより、異なるファイルで同じクラス名を使用しても衝突を気にする必要がなくなります。
CSSモジュールを使い始めるには、.module.css
拡張子の新しいファイルを作成し、app ディレクトリ内のコンポーネントにインポートします。
import styles from "./page.module.css";
export default function Home() {
return (
<div className={styles.page}>
<main className={styles.main}>
<h1>Hello, Next.js!</h1>
</main>
</div>
);
}
.main {
padding: 10px;
}
他のパスでも同様の CSS モジュールを作成しますが、上記と衝突しません。
import styles from "./page.module.css";
export default function Home() {
return (
<div className={styles.page}>
<main className={styles.main}>
<h1>Hello, Next.js!</h1>
</main>
</div>
);
}
.main {
padding: 20px;
}
グローバル CSS
グローバル CSS を使用すると、アプリケーション全体にスタイルを適用できます。グローバルスタイルを使用するには、新しい CSS ファイルを作成します。例えば、app/global.css というファイルを作成します。
グローバルスタイルをアプリケーションのすべてのルートに適用するには、ルートレイアウト (app/layout.js) でそのCSSファイルをインポートします。
import "./globals.css";
データフェッチ
サーバーコンポーネント
Fetch API を使用してデータを取得するには、コンポーネントを非同期関数に変換し、fetch 呼び出しを await します。Next.js で Server Componentsを使用して async/await を使うと、Next.js は動的レンダリングを選択します。これは、データが毎回サーバーでフェッチされ、ユーザーのリクエストごとにレンダリングされることを意味します。
import styles from "./page.module.css";
// 東京の緯度・経度
const LATITUDE = 35.6895;
const LONGITUDE = 139.6917;
export default async function Home() {
// データを取得する
const res = await fetch(
`https://api.open-meteo.com/v1/forecast?latitude=${LATITUDE}&longitude=${LONGITUDE}¤t_weather=true`
);
const wheather = await res.json();
const currentWeather = wheather.current_weather;
return (
<div className={styles.page}>
<p>
{currentWeather.weathercode === 0
? "東京の天気は晴れです。"
: "東京の天気は晴れではありません。"}
</p>
</div>
);
}
クライアントコンポーネント
React の useフックを使用して、サーバーからクライアントへデータをストリーミングできます。まず、サーバーコンポーネントでデータをフェッチし、そのプロミスをクライアントコンポーネントに props として渡します。サンプルコードはドキュメントを参照してください。
以下は単純に useState と useEffect のみを使用してデータフェッチを行う例です。
"use client";
import { useState, useEffect, use } from "react";
import styles from "./page.module.css";
// 東京の緯度・経度
const LATITUDE = 35.6895;
const LONGITUDE = 139.6917;
interface CurrentWeather {
weathercode: number;
}
export default function Home() {
const [currentWeather, setCurrentWeather] = useState<CurrentWeather>();
useEffect(() => {
const fetchWeather = async () => {
// データを取得する
const res = await fetch(
`https://api.open-meteo.com/v1/forecast?latitude=${LATITUDE}&longitude=${LONGITUDE}¤t_weather=true`
);
const weather = await res.json();
setCurrentWeather(weather.current_weather);
};
fetchWeather();
}, [currentWeather]);
return (
<div className={styles.page}>
<p>
{currentWeather?.weathercode === 0
? "東京の天気は晴れです。"
: "東京の天気は晴れではありません。"}
</p>
</div>
);
}
ルーティング
Link
<Link>
は、HTML の <a>
タグを拡張したビルトインコンポーネント(デフォルトで組み込まれているコンポーネント)で、プリフェッチとルート間のクライアントサイドナビゲーションを提供します。Next.js では、ルート間の移動において最も推奨される主要な方法です。
プリフェッチとは、リンクされるページのルートを事前に読み込むことです。
参考: How Routing and Navigation Works - 2. prefetching
import Link from "next/link";
export default async function Home() {
return (
<div className={styles.page}>
<Link href="/about">About</Link>
</div>
);
}
上記の遷移先に対応する page.tsx を作成します。
const About = () => {
return <div>Here is About page!</div>;
};
export default About;
これでリンク押下時、/about
にページ遷移ができるようになります。
useRouter
クライアントコンポーネントでは、useRouter フックを使用することができます。
"use client";
import { useRouter } from "next/navigation";
export default function Page() {
const router = useRouter();
return (
<button type="button" onClick={() => router.push("/about")}>
About
</button>
);
}
特定の要件(例えば動的にルートを変更する場合など)がない限り、Next.jsでは <Link>
コンポーネントを使用することが推奨されます。
redirect
サーバーコンポーネントでは useRouter は使えないので代わりに redirect を使用します。以下のコードでは 1/2 の確率でリダイレクトします。
import { redirect } from "next/navigation";
export default async function Home() {
const date = new Date();
const localSeconds = date.getSeconds();
// 現在の秒数が偶数ならリダイレクトを行う
if (localSeconds % 2 === 0) {
console.log("秒は偶数です。リダイレクトします。");
redirect("/about");
}
return <div>秒は奇数です。</div>;
}
起動中のターミナル(npm run dev
を実行したターミナル)には、以下のようなログが表示されます(サーバーコンポーネントなので devtools などのコンソールログには表示されません)。
秒は偶数です。リダイレクトします。
GET / 307 in 3071ms
GET /about 200 in 58ms
参考