はじめに
Next.jsとは?
Next.jsは、Reactを基盤としたJavaScriptフレームワークで、
Webアプリケーションを効率的に構築するための機能が豊富に揃っています。
特に、サーバーサイドレンダリング(SSR)や
静的サイト生成(SSG)を簡単に実現できる点が大きな特徴です。
従来、Reactではクライアントサイドでのレンダリングが基本でしたが、
Next.jsではサーバー側でページを生成し、
その結果をブラウザに送ることで、パフォーマンスが向上し、
SEO(検索エンジン最適化)に優れたWebサイトを作成できます。
フロントエンドフレームワークとは?
フロントエンドフレームワークは、
ユーザーが直接目にするWebページのデザインや動作を効率的に作るための道具です。Reactはその中でも最も広く使われているライブラリですが、
単体ではサーバーサイドでの処理やページ遷移の管理が難しく、
ページ全体を一度に表示する必要がありました。
Next.jsは、このReactを補完する形で、ページ遷移、
データフェッチ、SEOなどの機能を簡単に実装できるようにしており、
開発者がより生産的に作業できるようになります。
Next.jsの導入
Next.jsのインストール
Next.jsを使用するには、
まずNode.jsという環境をインストールします。
Node.jsは、サーバーサイドでJavaScriptを実行するためのソフトウェアで、
公式サイトから簡単にインストールできます。
次に、npxというツールを使ってNext.jsをインストールします。npx
はNode.jsに付属するツールで、
特定のバージョンのパッケージを簡単に実行できます。
以下のコマンドをターミナルに入力するだけで、
Next.jsプロジェクトをすぐに始められます。
npx create-next-app@latest my-next-app
コマンドが実行されると、
プロジェクト名(ここではmy-next-app
)のディレクトリに
Next.jsのファイルが自動で生成され、開発を開始できます。
プロジェクトの作成と初期設定
Next.jsのプロジェクトを作成した後は、開発サーバーを立ち上げて動作を確認します。まず、プロジェクトのディレクトリに移動し、次に以下のコマンドを実行します。
npm run dev
これにより、開発用のサーバーが立ち上がり、http://localhost:3000
にアクセスすることで、
Next.jsが正常に動作しているか確認できます。
フォルダ・ファイル構成の基本
フォルダ・ファイル構成
Next.jsのプロジェクトには
いくつかの重要なフォルダがあります。
基本的には以下のような構成です:
pages
フォルダ
ここに作成したファイルが自動的にURLに対応します。
たとえば、pages/about.tsx
というファイルを作ると、/about
というルートでそのページにアクセスできます。public
フォルダ
ここには画像やフォントなどの静的ファイルを置きます。
これらのファイルは、そのままWebページに組み込むことができます。styles
フォルダ
ここにはCSSやSassファイルなどのスタイルシートを置きます。
App RouterとPages Routerの違い
Next.jsにはルーティング方式が2種類あります。
- App Router
新しいバージョンで導入されたもので、
ルーティングがフォルダ構成に基づいて自動的に行われます。
これにより、ディレクトリベースでルートを柔軟に管理でき、
複数のレイアウトを簡単に適用できます。 - Pages Router
従来の方式で、pages
フォルダ内のファイルに基づいてルートが生成されます。
これもシンプルで扱いやすい方法ですが、
大規模アプリケーションでは柔軟性に欠ける場合があります。
Next.jsでのページとレイアウトの作成
ページファイル (page.tsx) の作成
Next.jsで新しいページを作成するには、pages
フォルダ内に.tsx
ファイルを作成します。
たとえば、about.tsx
というファイルを作ると、/about
というURLでそのページが表示されます。
この方法は非常にシンプルで、フォルダ内の構成がそのままURLに反映されるため、
直感的にルート管理ができます。
export default function About() {
return (
<div>
<h1>About Us</h1>
<p>Welcome to the about page!</p>
</div>
);
}
レイアウトファイル (layout.tsx) の作成
layout.tsx
ファイルは、
ページ全体で共通のデザインや構造を定義するために使います。
通常、ヘッダーやフッターなど、
複数のページで共通して使いたい要素をここに配置します。
以下は、基本的なレイアウトファイルの例です:
export default function Layout({ children }) {
return (
<div>
<header>My Website</header>
<main>{children}</main>
<footer>© 2024 My Website</footer>
</div>
);
}
テンプレートファイル (template.tsx) の使い方
template.tsx
は、
特定のページに共通のレイアウトやスタイルを適用するために使います。
たとえば、ブログ記事のテンプレートを作成し、それを複数のページで再利用できます。
Not Foundページやエラーページの作成
404エラーページやカスタムエラーページも簡単に作成できます。not-found.tsx
やerror.tsx
ファイルを用意することで、
特定のエラーが発生したときに表示されるカスタムページを設定できます。
スタイリングの方法
CSSモジュール
Next.jsでは、[name].module.css
という
形式のCSSファイルを使うことで、
モジュールごとにスタイルを分けることができます。
これにより、特定のコンポーネントだけにスタイルを適用することができ、
他のスタイルと競合しないように設計されています。
/* styles.module.css */
.container {
color: red;
}
Tailwind CSSの導入
Tailwind CSSは、ユーティリティクラスを活用して
効率的にスタイリングを行うCSSフレームワークです。
Next.jsでは、tailwind.config.js
ファイルを
追加することで簡単にTailwindを使用できます。
npm install tailwindcss
npx tailwindcss init
Next.jsのルーティング機能
動的ルーティング
Next.jsでは、pages
フォルダ内に
角括弧を使ったファイル名([id].tsx
など)を
作成することで、動的ルートを定義できます。
これにより、パラメータに基づいて異なるページを表示できます。
export async function getStaticPaths() {
return {
paths: [{ params: { id: '1' } }, { params: { id: '2' } }],
fallback: false,
};
}
export async function getStaticProps({ params }) {
return { props: { id: params.id } };
}
export default function Post({ id }) {
return <div>Post {id}</div>;
}
コンポーネントの作成と操作
リンクの実装 (Linkコンポーネント)
import Link from 'next/link';
export default function Home() {
return (
<div>
<Link href="/about">Go to About</Link>
</div>
);
}
オリジナルコンポーネントの作成
Next.jsでは、
Reactのコンポーネント作成を通じて、
独自のUI部品を作成し、
それをアプリケーション全体で再利用することができます。
コンポーネントとは、
ページやUIの一部を独立して管理する小さな部品で、
複数のページに共通の機能やデザインを適用できるものです。
例えば、ボタンやナビゲーションバー、カードコンポーネントなど、
特定のUI要素をコンポーネントとして切り出しておくことで、
コードの再利用性が高まり、メンテナンスがしやすくなります。
また、Reactの強力なステート管理機能を使って、
コンポーネントごとに動的なデータを扱うことも可能です。
基本的なコンポーネントの作成
まず、シンプルなボタンコンポーネントを作成してみましょう。components
フォルダ内に新しいファイルを作り、その中にコンポーネントを定義します。
// components/Button.tsx
import React from 'react';
interface ButtonProps {
label: string;
onClick: () => void;
}
export default function Button({ label, onClick }: ButtonProps) {
return (
<button onClick={onClick} style={{ padding: '10px 20px', backgroundColor: 'blue', color: 'white' }}>
{label}
</button>
);
}
このButton
コンポーネントは、label
とonClick
という2つのプロパティを受け取るシンプルなものです。label
はボタンに表示するテキスト、onClick
はボタンがクリックされたときに実行される関数です。
コンポーネントの使用
このボタンコンポーネントを使うには、
別のページやコンポーネントから呼び出します。
例えば、pages/index.tsx
にこのコンポーネントをインポートして使用します。
// pages/index.tsx
import Button from '../components/Button';
export default function Home() {
const handleClick = () => {
alert('Button clicked!');
};
return (
<div>
<h1>Welcome to Next.js</h1>
<Button label="Click Me" onClick={handleClick} />
</div>
);
}
このようにして、<Button>
コンポーネントをページ内に配置し、
クリックされたときにアラートを表示する簡単なイベントを作成できます。
スタイルの追加
コンポーネントにスタイルを追加する方法はいくつかあります。
たとえば、CSSモジュールを使うことでコンポーネントごとに
スタイルを分け、重複や衝突を防ぐことができます。
/* styles/Button.module.css */
.button {
padding: 10px 20px;
background-color: blue;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
}
.button:hover {
background-color: darkblue;
}
このCSSモジュールを使って、
先ほどのボタンコンポーネントをスタイル付けできます。
// components/Button.tsx
import React from 'react';
import styles from './Button.module.css';
interface ButtonProps {
label: string;
onClick: () => void;
}
export default function Button({ label, onClick }: ButtonProps) {
return (
<button onClick={onClick} className={styles.button}>
{label}
</button>
);
}
これにより、ボタンに専用のスタイルを適用し、
外部のスタイルや他のコンポーネントとの競合を防げます。
動的データを扱うコンポーネント
コンポーネントの強みは、静的なUIだけでなく、
動的なデータや状態(state)を管理できることです。
たとえば、カウントアップするボタンコンポーネントを作成してみます。
// components/CounterButton.tsx
import React, { useState } from 'react';
export default function CounterButton() {
const [count, setCount] = useState(0);
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>Click me</button>
</div>
);
}
ここでは、useState
フックを使って、
ボタンがクリックされた回数を状態として管理しています。
このように、コンポーネントは単純なUI部品から、
複雑なインタラクティブ要素まで多様な形で利用できます。
子コンポーネントと親コンポーネント
Next.jsでは、コンポーネントを階層構造で使用することも一般的です。
親コンポーネントが子コンポーネントにデータや関数を渡して、
それを基に動作する仕組みを構築できます。
例えば、以下のように親から子コンポーネントにプロパティを渡すことができます。
// components/Parent.tsx
import React from 'react';
import Child from './Child';
export default function Parent() {
const message = "Hello from Parent!";
return (
<div>
<h1>Parent Component</h1>
<Child message={message} />
</div>
);
}
// components/Child.tsx
import React from 'react';
interface ChildProps {
message: string;
}
export default function Child({ message }: ChildProps) {
return (
<div>
<h2>Child Component</h2>
<p>{message}</p>
</div>
);
}
ここでは、親コンポーネントParent
からChild
にmessage
というデータを渡し、
子コンポーネントで表示しています。
このようにして、コンポーネント間でデータを共有しながら、
複雑なUIを構築することができます。
データ操作とAPI
Next.jsでは、
データを操作し外部APIと連携するために
いくつかの強力な機能が提供されています。
これにより、サーバーサイドでのデータ取得や
クライアントサイドでの非同期処理を柔軟に行うことができます。
以下では、データフェッチだけでなく、
サーバーサイドコンポーネントとクライアントサイドコンポーネントの違いや、
APIルーティングの実装についても詳しく説明します。
データフェッチ (Fetch)
外部のAPIやバックエンドからデータを取得する際に、
Next.jsはfetch
関数やaxios
などを用いて
クライアントサイドでデータを取得することができますが、
サーバーサイドでもデータ取得を行うことができる点が特に便利です。
Next.jsには2つの代表的なデータ取得方法があります。
1. getStaticPropsgetStaticProps
は、
静的サイト生成(SSG)用にデータを事前に取得し、
ビルド時にHTMLファイルを生成します。
これは主にブログや商品リストなど、頻繁に更新されないデータに適しています。
// pages/posts.tsx
export async function getStaticProps() {
const res = await fetch('https://api.example.com/posts');
const posts = await res.json();
return {
props: { posts },
};
}
export default function Posts({ posts }) {
return (
<div>
{posts.map((post) => (
<div key={post.id}>
<h2>{post.title}</h2>
<p>{post.body}</p>
</div>
))}
</div>
);
}
この例では、getStaticProps
を使ってビルド時に外部APIからデータを取得し、
そのデータをprops
としてコンポーネントに渡しています。
ページがリクエストされるたびに再生成されないため、パフォーマンスが向上します。
2. getServerSidePropsgetServerSideProps
は、
ページリクエスト時にサーバーサイドで実行され、
リクエストごとにデータを取得します。
動的なデータやユーザー固有のデータに適した方法です。
// pages/profile.tsx
export async function getServerSideProps(context) {
const res = await fetch(`https://api.example.com/user/${context.params.id}`);
const user = await res.json();
return {
props: { user },
};
}
export default function Profile({ user }) {
return (
<div>
<h1>{user.name}</h1>
<p>{user.bio}</p>
</div>
);
}
この例では、ページがリクエストされるたびにサーバーでデータを取得し、
それをページに反映しています。
動的なデータやユーザー認証の必要なページに最適です。
サーバサイドコンポーネント vs クライアントサイドコンポーネント
Next.jsでは、
データ取得のタイミングによってコンポーネントを
サーバーサイドとクライアントサイドで使い分けることができます。
- サーバサイドコンポーネント(Server-Side Components)
サーバサイドコンポーネントは、
データをサーバーで取得し、
ページをレンダリングするため、
SEOに優れ、ページの初回表示速度が速くなります。
例えば、getServerSideProps
やgetStaticProps
で
取得したデータを使用してコンポーネントを表示する場合、
データはすでにサーバーサイドで処理されています。- 利点: SEOに強い、データの事前取得が可能
- 適用例: 記事一覧や製品情報ページなど、検索エンジンにインデックスされる必要があるページ
- クライアントサイドコンポーネント(Client-Side Components)
クライアントサイドコンポーネントは、
ブラウザでデータを取得し、
そのデータを基にUIをレンダリングします。
これは、インタラクティブなコンテンツや、
ユーザーの操作によってデータが動的に変わる場合に適しています。useEffect
フックを使ってデータを取得し、非同期処理を行います。- 利点: ユーザーインタラクションに優れ、データの動的取得が可能
- 適用例: ダッシュボードやフィードバックフォームなど、ユーザーに応じてデータが動的に変わるページ
// components/UserProfile.tsx
import { useState, useEffect } from 'react';
export default function UserProfile() {
const [user, setUser] = useState(null);
useEffect(() => {
async function fetchData() {
const res = await fetch('/api/user');
const data = await res.json();
setUser(data);
}
fetchData();
}, []);
if (!user) return <p>Loading...</p>;
return (
<div>
<h1>{user.name}</h1>
<p>{user.bio}</p>
</div>
);
}
APIルーティング
Next.jsは、
バックエンドAPIのルーティング機能も提供しています。
これにより、pages/api
ディレクトリにファイルを
作成するだけで簡単にAPIエンドポイントを作成でき、
サーバーサイド処理をNext.jsアプリケーション内に組み込むことができます。
例えば、pages/api/hello.ts
というファイルを作成すると、/api/hello
というエンドポイントが自動的に作られ、
GETリクエストを受け取れるようになります。
// pages/api/hello.ts
export default function handler(req, res) {
res.status(200).json({ message: 'Hello, Next.js!' });
}
このAPIは、Next.jsの開発サーバーまたは
本番サーバーでリクエストを受けることができ、fetch
関数やaxios
を使ってフロントエンドから呼び出すことができます。
APIルーティングを使うことで、認証、
データベースへのアクセス、外部サービスとの連携など、
サーバーサイドのロジックをNext.jsで実装することができます。
これにより、Next.jsでは
サーバーサイドとクライアントサイドの両方で
柔軟なデータ操作が可能になり、
複雑なWebアプリケーションでも
効率的にデータ管理ができるようになります。
また、Next.jsのAPIルーティングを使えば、
専用のバックエンドを持たずに
サーバーサイドの処理を行うことができるため、
フロントエンドからバックエンドまで一貫して開発できます。
その他の重要な機能
リダイレクトとミドルウェア
Next.jsでは、ページ遷移時のリダイレクトや、
ページアクセス前に特定の処理を挟むミドルウェアを簡単に実装できます。
認証やアクセス制限の管理に便利です。
メタデータの設定とSEO対策
Next.jsでは、next/head
を使ってページごとの
メタデータを設定することができます。
これは、SEO対策として非常に重要で、
ページのタイトルや説明文、OGPタグを設定することで
検索エンジンの評価を上げることができます。
まとめ
Next.jsは、
Reactをより使いやすく拡張したフレームワークで、
Web開発の生産性を向上させます。
この記事で基本的な知識を得たら、
小さなプロジェクトから始めて、
徐々にNext.jsの高度な機能に挑戦してみましょう!
公式ドキュメントやコミュニティのリソースも学習の助けになるので、
積極的に活用してください。
皆さんのお役に立てば幸いです!