フロントエンド実践 - ポートフォリオを作る
実際のWebサイトは、HTML, CSS, JavaScriptを直接書くだけでなく、Webフレームワークを利用して作られています。
今回は、JavaScriptのフレームワークであるNext.jsと、UIを補完するReactライブラリを使って、自分だけのポートフォリオサイトを作ります。
詳しい文法は、React References を適宜参照してください。

目次
Next.js入門
1-1. Webフレームワークとは
1-2. TypeScriptでの型定義
1-3. Next.jsプロジェクトの作成
1-4. ファイル構成
Next.jsの設計思想
2-1. ライフサイクルとは
2-2. RSC(React Server Components)
UIを作る
3-1. 画面全体の構成
3-2. メインページの作成
3-3. ヘッダー・フッターの作成
3-4. ページを増やす
3-5. faviconの設定
UIは状態(state)に依存する
4-1. ブラウザが状態を保持する仕組み
4-2. ライトモード・ダークモード対応の実装
4-3. Reactの状態管理
4-4. デベロッパーツールを使う
1. Next.js入門
1-1. Webフレームワークとは
Web開発に必要な機能が予めパッケージ化されたもの。さまざまなWebフレームワーク・ライブラリが生まれ、進化し続けている。
フロントエンド系フレームワーク
UIの更新、コンポーネント化、ルーティング、状態管理など。
- Next.js(UIでReactを使用、言語はJavaScript/TypeScript)
- Vue.js(JavaScript)
バックエンド系フレームワーク
DB接続、APIルーティング、セキュリティ対策(認証)など。
- Django, Flask, FastAPI(Python)
- Ruby on Rails(Ruby)
- Laravel(PHP)
- Express, NestJS(JavaScript/TypeScript)
TIP
フロントエンド
ユーザから見える部分、操作性。ボタンのデザイン、アニメーション、フォームの入力チェックなど。
バックエンド
ユーザに見えない裏側の部分。実際の処理やデータ管理、認証など。
1-2. TypeScriptでの型定義
Next.jsフレームワークを用いたフロントエンドの開発では、JavaScriptに型を追加した、TypeScriptが使われることが多い。
WARNING
TypeScriptは、フレームワークではなく言語!
「型」の重要性
型とは、コンピュータにデータの扱い方を教えるための、データが持つ性質のこと。整数型int、浮動小数点型float、文字列型string、真偽値boolなど。
コンピュータの型付けの方法は、言語によって異なり、次の2種類に分けられる。
動的型付け
実行時に型が決まる。型を書かなくて良い。Python, JavaScriptなど。
pythonx = 10 x = "hello"静的型付け
コンパイル時(実行前)に型が決まる。型を明示する必要があるが、コンパイル時にエラーを発見できる。TypeScript, Java, Cなど。
jslet x: number = 10; x = "hello"; // ←エラー
Web開発においては、次のような理由から型定義が重要である。
バグの早期発見
静的型付けでは、型が壊れるとコンパイルエラーになり、エラーの箇所を特定できるため、デバッグやリファクタリングが容易になる。
動的型付けでは、実行時に初めて
Undefinedなどのエラーが発生するため、バグの原因の特定が難しい。可読性/保守性の向上
型を明示するため、変数や関数がどのようなデータを扱うのか明確になる(可読性↑)。これにより、他人や将来の自分がコードを理解しやすくなる(保守性↑)。
TIP
リファクタリング
アプリケーションの外部から見た動作や機能を変えずに、内部のソースコードを整理・改善する作業。
1-3. Next.jsプロジェクトの作成
Node.jsバージョン確認
$ node -vで、Node.jsのバージョンが【20.11.1】以上であることを確認する。WARNING
Node.js をインストールしていない方は、1.開発環境の構築 を参照してください。
Next.js関連のコマンドのインストール
$ npm i -g create-next-appを実行して、Next.jsを立ち上げるためのコマンドをインストールする。
ターミナルで、プロジェクトのディレクトリを置く場所に移動
WARNING
WindowsでWSLを使用している方
Windows側のディレクトリ(
/mnt/c/...)で、4のコマンドを実行すると非常に遅くなります。WSLのホームディレクトリに移動し(
$ cd ~)、適当なディレクトリを作り、次に進みましょう。アプリケーションの作成
$ npx create-next-app@latest <プロジェクト名>を実行すると、Next.js製アプリの雛形(Next.js + React + 設定済み環境)が作られる。

依存関係のインストール(後述)
$ npm install開発用サーバ
localhostの起動アプリのディレクトリ下(
/portfolio)で、以下を実行する。$ npm run dev
TIP
ポート番号を自分で指定したい場合は、
$ npm run dev -- --port 3001とする。
ブラウザで、
http://localhost:3000にアクセスし、次の画面が表示されれば完了。
1-4. ファイル構成
VSCode で、先ほど作成したportfolioディレクトリを開いて、Next.js製プロジェクトのファイル構成を確認する。
TIP
先ほどのターミナルで、/portfolio にいる状態で、
$ code .
を実行すれば、一発でVSCodeに飛べる。
appディレクトリ
ブラウザでlocalhost:3000にアクセスした際に描画された画面のHTMLは、app/layout.tsxおよびapp/page.tsx内に書かれている。

→ 実際に、この中身を編集していく。
package.json
中身は次のようになっている。
{
"name": "my-app",
"version": "0.1.0",
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start"
},
"dependencies": {
"next": "14.x",
"react": "18.x",
"react-dom": "18.x"
}
}チーム開発では、この設計書を他者がクローン(次回!)することで、プロジェクトの実行環境を再現することができる。
"script"
コマンドのショートカット一覧。例えば
npm run devは、json"scripts": { "dev": "next dev" }なので、
next devを実行しているのと同じ。"dependencies"
Next.jsプロジェクトは、外部ライブラリ(Reactなど)に依存している。その外部ライブラリのバージョンを明記しておく。
この依存関係(外部ライブラリのバージョン情報)の通りに、ライブラリ・モジュールをインストールするコマンドが以下。
$ npm installこれを実行すると、
node_modules(= インストールしたライブラリの実体)が作られる。
TIP
「外部ライブラリ」はどこにある?
インターネット上のnpmというサーバに、何百万個ものライブラリが置かれている。世界中の人が作ったコードが、無料で置いてある。React, Next, Axios, Lodashなど。
$ npm install で、どこかのnpmサーバにHTTPでアクセスしている。
TIP
JSON(JavaScript Object Notation)
データフォーマット(≠プログラミング言語)の一つ。{ key: value } の書き方をする。
{
"name": "骨なしチキン",
"age": 19
}という書き方をする。JSONの他には、XML(HTMLのようにタグで囲む記法)、YAML(YAML Ain't Markup Language)などがある。
2. Next.jsの設計思想
Next.jsは、既存のReactという革命的なUIライブラリを使って、Webアプリケーションを開発できるようにした、フレームワークである。
TIP
UI(User Interface)
Webアプリ・サービスと、ユーザーとを繋ぐ接点。つまり画面操作、ボタンや文字、そのデザインのこと。UIを通じて得られる「体験・使い心地」全体をUX(User Experience)という。
2013年、旧Facebook社がReactライブラリを開発し、「UIをコンポーネントで組む」という概念が生まれた。
2016年、この設計思想をWebアプリ開発で実用化すべく、Vercel社がNext.jsフレームワークを開発した。
2-1. ライフサイクルとは
Webページにおける処理がどういう順番で進んで、どのタイミングで何が起こるか、という流れのこと。
一般的に、フロントエンドのライフサイクルは、次のようになる。
クライアントがリクエストを送信(URLが叩かれる)
サーバがレスポンスを返す(HTML, CSS, JS)
ブラウザが、HTMLをパースしてDOM生成
CSSを適用
レンダリング
JSが実行される
ユーザの操作によっては再レンダリング
これは、2〜6がクライアントが行われるため、①データ取得が遅い、②セキュリティ的にクライアントに寄っている、という問題点がある。
特に、「6. JSが実行される」においては
- データ取得(
fetch) - UI組み立て
- イベント処理(クリックなど)
- DOM更新
これら全てのロジックがクライアント側で実行されるため、データ取得の完了を待つ必要があり、初期表示が遅くなる。
2-2. RSC(React Server Components)
前述の問題を解消するため、Next.jsフレームワークは、Reactライブラリの思想を拡張+実用化した。
UI(状態)をコンポーネントで分割して管理する(Reactライブラリが持っていた思想)
そのうち「データ取得+UI生成」のJSをサーバ側で実行する(= Server Components)
ブラウザは、返ってきたServer Componentsの表示(JSは返ってこない)、Client ComponentsのJS実行、その他イベント処理をするだけ
というライフサイクルが実現した。
TIP
Client Components
ブラウザ側でJSが実行されるもの。プログラム内で "use client"; と明示する。
3. UIを作る
3-1. 画面全体の構成
一般的なWebページは、ヘッダー・コンテンツ・フッターで構成されている。
ヘッダー:サイト名、ロゴ、ナビゲーションメニューなどを表示する領域
コンテンツ:ページごとに内容が変わる中心部分
フッター:コピーライト、問い合わせ先、補助リンクなどを表示する領域

画面遷移を伴うWebサイトでは、ヘッダーやフッターを共通コンポーネントとして切り分け、複数ページで再利用する。
これにより、デザインを統一化しやすくなり、修正も1箇所で済むようになる。
3-2. メインページの作成
Next.js(React)においては、次のように、すべてのUIがコンポーネント(関数)として設計されている。戻り値にHTMLが書かれる。
export default function xxx() {
return ...
}JSの export とは
const msg = "hello";と変数を宣言したとき、これは他のファイルからは見えない。これを、
// file.js
export const msg = "hello";とすることで、
// file2.js
import { msg } from "./file.js";
console.log(msg); // helloのように外部ファイルで import して参照できるようになる。
export, import によって、コンポーネントの依存関係(= どこでどのコンポーネントを再利用するか)を管理できる。
メインページのコンポーネントを編集
app/page.tsx の Home() 内を編集し、メインページを構築する。
export default function Home() {
return (
<div className="intro-wrap">
<h1 className="intro-title">自己紹介</h1>
<p className="intro-lead">はじめまして、骨なしチキンです!</p>
<section className="intro-section">
<h2>取り組んでいること</h2>
<ul className="intro-list">
<li>Web開発</li>
<li>プログラミングサークルの運営</li>
</ul>
</section>
<section className="intro-section">
<h2>SNS</h2>
<ul className="intro-list">
<li>
<a href="https://x.com/74rina_">Xアカウント</a>
</li>
</ul>
</section>
</div>
);
}className で要素を指定し、そこに当てるCSSを/app/globals.cssに書く。
@import "tailwindcss";
body {
color: var(--foreground);
background: var(--background);
font-family: Arial, Helvetica, sans-serif;
margin: 0;
}
main {
min-height: calc(100vh - 160px);
}
.intro-wrap {
max-width: 760px;
margin: 0 auto;
padding: 24px 16px 64px;
line-height: 1.7;
}
.intro-title {
font-size: 36px;
margin: 0 0 12px;
}
...3-3. ヘッダー・フッターの作成
app/layout.tsx の RootLayout() 内を編集し、ヘッダー・フッターのコンポーネントとする。
export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
return (
<html lang="en">
<body className={`${geistSans.variable} ${geistMono.variable} antialiased`}>
<header className="flex h-20 items-center justify-between border-b border-neutral-200 px-6">
<p className="text-base font-semibold">ポートフォリオ</p>
<nav className="flex items-center gap-4 text-sm text-neutral-600">
<a className="hover:text-neutral-900" href="/">
Home
</a>
</nav>
</header>
<main>{children}</main>
<footer className="flex h-20 items-center justify-center border-t border-neutral-200 px-6 text-sm text-neutral-500">
<p>© 2026 骨なしチキン</p>
</footer>
</body>
</html>
);
}className=" "に直接CSSを書くことも可能(インラインスタイルシート)。{children}の中身には、app/page.tsxで default export した関数(今回はHome())の戻り値が埋め込まれる。これはNext.jsの仕様。
つまり、次のようにページが構築されている。
<RootLayout>
<Home />
</RootLayout>
3-4. ページを増やす
自己紹介の Home ページに加えて、作品を置く Works ページを作る。
app/配下にworksというディレクトリを新規作成し、その中にpage.tsxというファイルを作成する。その中に、Worksページのコンポーネントを次のように作成する。
export default function WorksPage() {
return (
<div className="intro-wrap">
<h1 className="intro-title">作品</h1>
<p className="intro-lead">骨なしチキンの制作物をまとめます。</p>
<section className="intro-section">
<h2>Qiita記事</h2>
<a href="https://qiita.com/74rina_/items/415208959e7616e427a6">
TryHackMeの解説記事
</a>
<br></br>
<a href="https://qiita.com/74rina_/items/b87fc9e7036a5cb30d77">
自作OSにファイルシステムを実装する
</a>
</section>
</div>
);
}また、ヘッダーにWorksへのリンクを追加する。
<a className="hover:text-neutral-900" href="/works"> Works </a>
3-5. faviconの設定
faviconとは、ブラウザのタブのアイコンのこと。
![]()
通常は、HTML内に
<link rel="icon" href="favicon.ico" />を追加することで設定できる。
今回は、Next.jsがデフォルトでfavicon.icoの設定スクリプトを用意しているため、画像を差し替えるだけで良い。
TIP
拡張子 .ico
普通の画像(png, jpg)ではなく、アイコン用の拡張子。
複数のサイズ(16*16, 32*32など)を1ファイルに入れられる。状況に応じて最適なサイズを使ってくれる。
背景透過に対応。
などの機能を持つ。
4. UIは状態(state)に依存する
4-1. ブラウザが状態を保持する仕組み
WebサイトのUIは、状態(state)に依存することを意識する。
INFO
状態とは
ユーザーがログイン中か、新たに投稿されたか、カートに追加されたか、などを表す変数のこと。
そこで、クライアント(ブラウザ)とサーバの双方が、状態を管理する仕組みを持っている。状態をどの期間・範囲まで永続化させるのか、も重要。
以下は、ブラウザが保持する情報(変数)の種類と、その永続範囲である。
| 種類 | タブ閉じたら | リロードしたら | タブ間共有 |
|---|---|---|---|
| Cookie | △(期限次第) | ○ | ○ |
| localStorage | ○ | ○ | ○ |
| sessionStorage | × | ○ | × |
| メモリ | × | × | × |
4-2. ライトモード・ダークモード対応の実装
画面のライトモード・ダークモードを切り替えられるボタンを実装する。
本質は、「ページをリロードしたり、タブを閉じたりしても、ライト・ダークの設定が適用されたままにする」部分である。
値の永続範囲が最も大きい localStorage を、次のように使用する。
過去の設定(ライト or ダーク)を取得
tsxuseEffect(() => { const storedTheme = localStorage.getItem(storageKey); ... }, []);その設定を適用させる
tsxconst initialTheme = storedTheme ?? (media.matches ? "dark" : "light"); setTheme(initialTheme); applyTheme(initialTheme);ユーザーがボタンを押して切り替えた瞬間
tsxlocalStorage.setItem(storageKey, nextTheme);
4-3. Reactの状態管理
UIは状態に依存している(再掲)。
- 状態(state)が変わる
- UIが自動で再計算される
Reactでは、これを次のように書くことができる。
function Counter() {
const [count, setCount] = useState(0);
return <button onClick={() => setCount(1)}>{count}</button>;
}
s;TIP
Reactの useState()
引数 :初期値
戻り値:[現在の状態, 状態を更新する関数]
const [state, setState] = useState(initialValue);「状態を更新する関数」が呼ばれた瞬間、Reactは再レンダリングを行う。すなわち、function Counter()が再度実行される。
4-4. デベロッパーツールを使う
Cookie や localStorage などの「ブラウザ自体が持つ値」を調べたり、ブラウザが送信するリクエスト・レスポンスの中身を見たりするときは、ブラウザのデベロッパーツール(開発者ツール)を使う。
(Windows)
Ctrl + Shift + I(Mac)
Cmd + Option + I
TIP
Web開発の途中でエラーが発生したら、まずは開発者ツールを開いて、エラーログを読む!
Networkタブ
ブラウザが、サーバとやり取りした結果を見ることができる。
以下は、サーバから受け取ったファイル一覧。

また次のように、ファイルの中身(以下はHTTPレスポンスヘッダ)も確認できる。

Applicationタブ
ブラウザの記憶装置のもつ値を、すべて見ることができる。

Consoleタブ
JavaScript実行環境(> の行が入力部分)。

Web開発では、プログラム内に
console.log("debug log");とデバッグログを入れておき、開発者ツールのConsoleタブで、そのログを確認する。
お疲れさまでした!
今回の成果物に対し、
UI、Webデザインを凝ってみる
CSSでアニメーションをつける
などの拡張を施し、ぜひ自分のポートフォリオサイトを公開してみましょう!
このような静的サイトは、GitHub Pages で簡単にデプロイすることができます。
参考文献
Zenn Next.jsの考え方
Qiita Reactにおける状態管理(State)の基礎