Next.js(App Router)とReactでよく使う内容をわかりやすくまとめてみた
Next.jsとは
Vercelによって開発されているReactベースのフレームワークです。
クライアントサイドレンダリング(CSR)、SSR(サーバーサイドレンダリング)、SSG(静的サイト生成)、ISR(インクリメンタルスタティックリジェネレーション)が含まれ、SPA(シングルページアプリケーション)の開発もサポートしています。Next.jsを使用することで、これらの異なる手法をプロジェクトのニーズに応じて選択し、組み合わせることができます。
Hot Module Replacement
Next.jsはデフォルトでHMR(Hot Module Replacement)をサポートしているため、コードの変更が即座にブラウザに反映されます。
プロジェクトの作成
デフォルトでTypeScriptが同梱されるようになったので --ts
は不要です。
今回はnpmを使用して最新のバージョンでプロジェクトを作成します。
npx create-next-app@latest <プロジェクト名>
npx create-next-app@13 <プロジェクト名> # バージョンを指定する方法、今回は13
プロジェクト名に大文字は使用できません。使用した場合、以下のエラー文が表示されます。
name can no longer contain capital letters
途中質問されますので答えます。
Ok to proceed? (y) # yと入力
✔ What is your project named? … test # 先ほどプロジェクト名を入力していない場合はここで入力
✔ Would you like to use TypeScript? … No / Yes # Yesを選択
✔ Would you like to use ESLint? … No / Yes # Yesを選択
✔ Would you like to use Tailwind CSS? … No / Yes # Yesを選択
✔ Would you like to use `src/` directory? … No / Yes # Yesを選択
✔ Would you like to use App Router? (recommended)? … No / Yes # Yesを選択
? Would you like to customize the default import alias (@/*)? › No / Yes # Yesを選択
? What import alias would you like configured? › @/* # enter
package.jsonには以下が追記されています。
{
{
"name": "clasp",
"version": "0.1.0",
"private": true,
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "next lint"
},
"dependencies": {
"react": "^18",
"react-dom": "^18",
"next": "14.0.1"
},
"devDependencies": {
"typescript": "^5",
"@types/node": "^20",
"@types/react": "^18",
"@types/react-dom": "^18",
"autoprefixer": "^10.0.1",
"postcss": "^8",
"tailwindcss": "^3.3.0",
"eslint": "^8",
"eslint-config-next": "14.0.1"
}
}
開発環境を起動
npm run dev
http://localhost:3000/ にアクセスして以下の表示を確認してください。
HTTPSで立ち上げる方法
package.jsonのdevスクリプトに--experimental-https
を追記してください。
"scripts": {
"dev": "next dev --experimental-https",
あとはいつも通り開発環境を起動してください。
npm run dev
Vercelにデプロイする
GitHubのリポジトリを作成は済ませておいてください。
下記の手順でGitHubのリポジトリをVercelに連携させます。
Vercelにログインしてください。
Add New...
を選択
Project
を選択してください。
デプロイしたいリポジトリのImport
を選択
Deploy
を選択してください。
Continue to Dashboard
を選択
Visit
を選択すると、デプロイされたページにアクセスできます。
next/link
主に静的なリンク、つまり事前に定義された、変更されないURLにユーザーを送るために使われ、HTMLの <a>
タグのように使います。
また、リンクがビューポート(ユーザーの画面に表示されている範囲)に入ると、リンクの先のページに必要なJavaScriptやデータのプリフェッチング(バックグラウンドでデータを読み込み始める)を自動的に行うので、ページ間のナビゲーションを非常に高速にします。
import Link from 'next/link';
export default function Navbar() {
return (
<nav>
<Link href="/about"><a>About</a></Link>
<Link href="/services"><a>Services</a></Link>
<Link href="/contact"><a>Contact</a></Link>
</nav>
);
}
next/router
ユーザーのアクションやアプリケーションの状態に基づいて動的にページ遷移を行いたい場合に使います。例えば、フォームの送信後にユーザーを特定のページにリダイレクトしたり、特定の条件下で自動的にユーザーを異なるページに遷移させる場合などです。
next/router
は、next/link
とは異なり、自動的にプリフェッチングを行わず、ページ遷移をトリガーすると、その時点で必要なリソースの読み込みが始まります。
useRouter
フックを通じてアクセスでき、push
やreplace
などのメソッドを提供し、コード内から直接URLを操作できます。
import { useRouter } from 'next/router';
export default function Login() {
const router = useRouter();
const login = () => {
// ログイン処理後に/homeにリダイレクトする
router.push('home');
}
return (
<button onClick={login}>Log In</button>
);
}
.env
.gitignoreファイルに.env
を追記してください。
.env
Next.js は、サーバーサイドでのみ使用される環境変数とクライアントサイドで利用する環境変数を区別しています。
サーバーサイド
ENDPOINT='https://example.com/'
const endPoint = process.env.ENDPOINT || '';
クライアントサイド
環境変数を使用する('use client';
など)には、環境変数名をNEXT_PUBLIC_
で始める必要があります。
NEXT_PUBLIC_ENDPOINT='https://example.com/'
const endPoint = process.env.NEXT_PUBLIC_ENDPOINT || '';
静的ファイルの生成を行う
next.config.mjs
に下記を追記して静的エクスポートを有効にしてください。
/** @type {import('next').NextConfig} */
const nextConfig = {
output: 'export', // 追記
};
export default nextConfig;
その後下記のコマンドで静的なHTMLファイルとして出力します。
npm run build
プロジェクト直下にoutというフォルダができたことを確認してください。
前回作成したファイルがある場合でもoutフォルダ内の内容は新しく生成された静的ファイルで上書きされるので問題ありません。
'use client';という指示子を使用している場合
'use client';
という指示子は、Next.js 12.2以降で導入された新しい機能、Server ComponentsとClient Componentsを区別するためのものです。'use client';
をファイルの最初に記述することで、そのコンポーネントがクライアントサイドでのみ実行されることをNext.jsに指示します。
またNext.jsにおいてクライアントサイドで安全に使用できる環境変数は、変数名がNEXT_PUBLIC_
で始まるものに限られます。
上記の理由から次のように記述して使用してください。
NEXT_PUBLIC_ENDPOINT='https://example.com/'
.gitignoreファイルにも.env
を追記
.env
const endPoint = process.env.NEXT_PUBLIC_LAMBDA_ENDPOINT || '';
APIハンドラの変更点
APIハンドラにおいてfetchAPI
に似た新しいResponse
オブジェクトが導入されています。従来の res.status().json()
などのメソッドチェーンを使う代わりに、new Response()
コンストラクタやユーティリティ関数を使ってレスポンスを生成する方法に変更されています。
useRouter
Unhandled Runtime Error Error: NextRouter was not mounted. と表示される場合
下記が表示される場合の解決方法です。
Unhandled Runtime Error Error: NextRouter was not mounted. https://nextjs.org/docs/messages/next-router-not-mounted
下記のように記述していないか確認してください。
import { useRouter } from 'next/router';
appディレクトリではnext/router
ではなくnext/navigation
からuseRouter
をimportする必要があるためエラーが表示されます。import先を修正してください。
import { useRouter } from 'next/navigation';
favicon
app以下にicon.ico
というファイル名で作成するとローカルでも反映されます。
imgタグのESLint警告
Next.jsで、imgタグを使うと以下のような警告が出ることがあります。
Using <img> could result in slower LCP and higher bandwidth. Consider using <Image /> from next/image to automatically optimize images. This may incur additional usage or cost from your provider. See: https://nextjs.org/docs/messages/no-img-element",
このエラーメッセージは、Next.jsプロジェクトで<img>要素を使用して画像を表示している場合に表示されます。Next.jsでは、画像の最適化とパフォーマンス向上のために、<Image>コンポーネントを使用することが推奨されています。
@next/next/no-img-elementのエラー
例えばS3にデプロイする場合などにnext/imageを使用しているとうまく表示されないので、<img> タグを使用するようにしました。
<img> タグを使用すると@next/next/no-img-element
のエラーがエディター上に表示されるのでESLintの設定ファイルに追記します。
{
"extends": "next/core-web-vitals",
"rules": { //追記
"@next/next/no-img-element": "off" //追記
}
}
page.module.css
CSS Modulesではグローバルセレクター(body, id セレクタなど)の使用がスタイルの衝突を避けるために制限され、スタイルは主にクラスセレクタを通じて適用されます。
指定した場合は下記のようなエラーが出ます。
Syntax error: Selector "body" is not pure (pure selectors must contain at least one local class or id)
解決方法としては要素に特定のclassName
を使用するか、このルールをグローバルCSSファイルに記述する方法があります。
また、下記のようにクラスセレクタ(この場合は .name)と組み合わせることで記述することができます。
.name input[type='text'] {
width: 100%;
}
下記の場合はどちらもクラスを記述してください。
.btn button[type='submit']:active::after,
.btn button[type='submit']:hover::after {
width: 100%;
}
globals.css
プロジェクト全体にわたって直接適用されるスタイルを定義します。
.name
のようなクラスは記述してもエラーにはなりませんが、効きません。page.module.cssに記述してください。
末尾のスラッシュを有効にする
trailingSlashの設定を追加してください。
設定を有効にすることで設定前は、/about/
は/about
にリダイレクトされていますが、/about
のようなURLは/about/
へリダイレクトされるようになります。
module.exports = {
trailingSlash: true, //追記
}
Next.jsアプリケーションでサードパーティのスクリプトやiframeを効率的に扱うためのユーティリティやコンポーネントを提供するライブラリ
style属性
に記述する必要があるので、page.module.css
などのスタイルは適用されませんでした。
またiframeタグ
で使用する下記についても記述できず、自動で再生することができませんでした。
ループ再生やミュートでの再生はparam属性
に記述すれば問題ありませんでした。
frameBorder='0'
allow='accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture'
allowFullScreen
iframeタグで[Violation] Forced reflow while executing JavaScript took 35msと表示される場合
loading='lazy'
を追記してください。
遅延読み込みを利用することで、ページロード時に実行されるJavaScriptの量を減らし、それによって発生する可能性のある強制的なレイアウト再計算(Forced Reflow)を避けることができます。
<iframe
width=''
height=''
src=''
frameBorder='0'
allow='accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture'
allowFullScreen
loading='lazy'
></iframe>
環境によってパスを変更したい場合
主にサブディレクトリにNext.jsのプロジェクトを置く場合のpublic
以下のimage
の呼び出しなどで使用すると思います。
.env.developmentと.env.productionの.envファイルを作成します。
それぞれの環境変数は下記の場合に使用されます。これを使用して環境によってパスを変更します。
.env.development
はnpm run dev
実施時(ローカル環境)
.env.production
はnpm run build
実施時(本番=静的な生成を行った場合)
.それぞれの.envファイルの記述
NEXT_PUBLIC_BASE_PATH =/
BASE_PATH =/
NEXT_PUBLIC_BASE_PATH =/subdirectory-name
BASE_PATH =/subdirectory-name
使用例
const basePath = process.env.NEXT_PUBLIC_BASE_PATH || '';
<img
src={`${basePath}/images/icon.png`}
/>
Reactとは
UI(ユーザーインターフェース)を構築するためのオープンソースのJavaScriptライブラリです。Facebookによって開発されています。
Reactの主な特徴は下記の5点かなと思います。
コンポーネントベースのアーキテクチャ:
UIを独立した再利用可能なコンポーネントに分割して構築します。これにより、コードの保守性が向上し、開発プロセスが効率化されます。
宣言的なビュー
宣言的にビューを記述することで、アプリケーションの各状態に対応するビューを設計できます。これにより、コードの可読性が向上します。
仮想DOM
仮想DOMを使用することで、実際のDOMに変更が必要な場合にのみ、最小限の操作で更新を行います。これにより、アプリケーションのパフォーマンスが向上します。
JSX
JSXと呼ばれるXML風の構文(例: const element = <h1>Hello, world!</h1>;
)を使用して記述するので、見た目に関するコードが書くやすく、見やすくなっています。
データフローの単一方向性
データが一方向に流れる設計を採用しています。例えば、コンポーネントの状態(state)が変更されると、その変更が親コンポーネントから子コンポーネントへと反映されます。
これにより、アプリケーションのデータフローが予測しやすく、管理しやすいです。
React.memo
また下記のように2通りの記述方法があります。
デフォルトエクスポート(Default Export)
プロップスを受け取らないfooterのような完全に静的なコンテンツを表示する場合に適しています。
import React from 'react';
const Modal = () => {
return (
<div className={styles['modal']}>
<p>これはモーダルです</p>
</div>
);
};
export default React.memo(Modal);
名前付きエクスポート(Named Export)
propsがある場合は型を定義して、TypeScriptを使用している場合に型安全が向上させた方が良いです。
import React, { memo } from 'react';
export interface ModalProps {
id: string;
}
export const Modal = memo(({ id }: ModalProps) => { return (
<div className={styles['modal']}>
<p>これはモーダルです</p>
</div>
);
};
Modal .displayName = 'Modal';
Suspense
SuspenseはReact 16.6以降で利用可能です。
ReactのSuspenseコンポーネントは、Reactが提供する機能で、非同期操作(データフェッチ、遅延ローディングのコンポーネントなど)をより簡単に扱うための仕組みです。Suspenseを使用することで、非同期に読み込まれるコンポーネントやデータが利用可能になるまでの間、フォールバックとして任意のReact要素(例えばローディングインジケータ)を表示することができます。
React.lazyと組み合わせて使うことで、コンポーネントを非同期にロードし、初期ページロード時のJavaScriptファイルのサイズを小さく保つことができます。これにより、パフォーマンスを向上させることが可能です。
fallback={<div>Loading...</div>}
は記述しなくてもエラーになりません。
import React, { Suspense } from 'react';
// 非同期で読み込むコンポーネント(React.lazyを使用)
const ModalComponent = React.lazy(() => import('./ModalComponent'));
function App() {
return (
<div>
<Suspense fallback={<div>Loading...</div>}>
{/* ModalComponentが読み込まれるまで、"Loading..." が表示される */}
<ModalComponent />
</Suspense>
</div>
);
}
useContext コンポーネント間のデータ共有
詳しく下記の記事で書いています。
Attempted import error: 'useForm' is not exported from 'react-hook-form' (imported as 'useForm').
react-hook-form
はフォーム管理ライブラリであり、主にクライアントサイドでのユーザーインタラクションを扱います。
よって、'use client';
ディレクティブを Next.jsのファイルの先頭に記述することにより、そのファイルがクライアントサイド専用であると Next.js に明示的に指示すると解決すると思います。
終わりに
何かありましたらお気軽にコメント等いただけると助かります。
ここまでお読みいただきありがとうございます🎉
Discussion