こんにちは、株式会社ファストコーディングのフルスタックエンジニア、独身貴族Fireです。
最近、ギターの練習で「Am7のコードを押さえて」とAIに聞いたら、フレットの図が返ってきました。正確だけど、そこじゃない。「初心者がFコードの代わりに使える省略フォームで」と聞き直したら、ちゃんと実用的な回答が来た。
聞き方が曖昧だと、正しいけど使えない答えが返ってくる。コードも同じです。
AI駆動開発において、プロンプトの設計が出力品質の8割を決めると私は考えています。同じClaude、同じCursor、同じCopilotを使っていても、チームメンバーによって出力の質が全く違う。その差はツールの使い方ではなく、「聞き方」にあります。
今回は、React/Next.jsの開発に特化したプロンプト設計のTipsを、実例のBefore/Afterで紹介します。
なぜ「ログインフォームを作って」ではダメなのか
まず、典型的な失敗例から見ていきます。
Bad:漠然としたプロンプト
ログインフォームをReactで作ってください。AIはこう返してきます。
// AIの出力:動くが「使えない」コード
import { useState } from 'react';
export default function LoginForm() {
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault();
console.log({ email, password });
};
return (
<form onSubmit={handleSubmit}>
<input
type="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
placeholder="Email"
/>
<input
type="password"
value={password}
onChange={(e) => setPassword(e.target.value)}
placeholder="Password"
/>
<button type="submit">Login</button>
</form>
);
}構文エラーはない。型も付いている。でもこのコードは案件では使えません。理由は以下の通りです。
- バリデーションがない(空送信できる)
- エラー表示がない(ユーザーに何も伝わらない)
- ローディング状態がない(二重送信のリスク)
- アクセシビリティ対応がない(labelがない、エラーとフィールドの関連付けがない)
- APIとの接続がない(console.logで終わっている)
Good:要件を構造化したプロンプト
同じ「ログインフォーム」でも、プロンプトを構造化すると出力が劇的に変わります。
Next.js App Router(TypeScript)でログインフォームを実装してください。
【機能要件】
- メールアドレスとパスワードの入力
- クライアントサイドバリデーション(メール形式チェック、パスワード8文字以上)
- Server Actionでのフォーム送信
- ローディング状態の表示
- エラーメッセージの表示(フィールドごと+フォーム全体)
【非機能要件】
- アクセシビリティ:label、aria-describedby、aria-invalid対応
- UX:touched状態を管理し、未入力フィールドにはエラーを出さない
- スタイル:Tailwind CSSでシンプルに
【制約】
- 外部ライブラリ(react-hook-form等)は使わない
- useActionStateを使用する
- 'use server'ファイルにはasync関数のみexportするこのプロンプトから返ってくるコードは、バリデーション、ローディング、アクセシビリティ、Server Action対応が最初から含まれています。
プロンプト設計の3つの原則
私がReact/Next.js開発でAIを使う際に意識しているプロンプト設計の原則を3つ紹介します。
原則1:「What(何を)」ではなく「Context(文脈)」を伝える
AIに伝えるべき情報は「何を作るか」ではなく、その機能が置かれる文脈です。
| 伝えるべき項目 | 例 | 効果 |
|---|---|---|
| フレームワーク・バージョン | Next.js 15, App Router, TypeScript | 廃止されたAPIを使わなくなる |
| データの流れ | Server Component → Client Component | propsのシリアライズ制約を考慮する |
| 既存の設計パターン | zustandでグローバル状態管理 | 既存アーキテクチャと整合するコードになる |
| ユーザーの操作フロー | フォーム入力 → バリデーション → 送信 → リダイレクト | UXを考慮した実装になる |
たとえば「ドロップダウンメニューを作って」ではなく、こう聞きます。
Next.js App RouterのServer Component内に配置する
ナビゲーションドロップダウンを実装してください。
【文脈】
- ヘッダーのServer Componentから呼ばれるClient Component
- メニュー項目はサーバーから取得済みで、propsとして渡される
- 項目数は最大10件、各項目はlabelとhrefを持つ
- モバイルではハンバーガーメニューに切り替わる(768px以下)
【技術要件】
- 'use client'宣言が必要
- キーボード操作対応(Escape、Arrow Up/Down)
- 外側クリックで閉じる
- Tailwind CSSでスタイリングこの「文脈」があるかないかで、返ってくるコードの実用性が全く違います。Server ComponentとClient Componentの境界を理解した上でのコードが返ってきますし、propsの型もserializable(シリアライズ可能)な形になります。
原則2:「制約」を先に伝える
AIは何も制約を与えないと、「もっともらしいが使えない」コードを出す傾向があります。制約を先に伝えることで、出力の方向性を限定できます。
特にReact/Next.js開発で効果が高い制約は以下の通りです。
【必ず先に伝える制約の例】
❌ 使わないもの:
- 外部ライブラリを使わない(またはXXライブラリのみ使用可)
- class componentは使わない
- anyは使わない
✅ 使うもの:
- App Router(Pages Routerではない)
- Server Actions(API Routeではない)
- useActionState(useFormStateは廃止)
- TypeScript strict mode
⚠️ 気をつけること:
- 'use server'ファイルからはasync関数のみexport可
- Server ComponentからClient Componentにはserializableなpropsのみ渡せる
- Next.js 15ではparamsがPromiseになったAIが古い情報に基づいたコードを出すのは、こうした制約を伝えていないことが原因です。ReactのuseFormStateがuseActionStateにリネームされたこと、paramsがPromiseになったことなど、直近のAPI変更は特に指定しないと古い記法が返ってきます。
原則3:「段階的に」指示を出す
一度に完璧なコードを求めるより、段階的に指示を出す方が品質が高くなります。
ステップ1:型定義から始める
以下の要件のログインフォームを作ります。
まず、必要な型定義(Props、State、Action)だけを出力してください。
コンポーネント本体はまだ不要です。ステップ2:型をレビューしてからコンポーネントを依頼
型定義をレビューしました。以下の修正を加えます。
(修正内容)
この型に基づいて、コンポーネント本体を実装してください。ステップ3:テストやエッジケースを追加
実装を確認しました。以下のエッジケースに対応してください。
- パスワードが空白のみの場合
- メールアドレスに日本語が含まれる場合
- API呼び出しがタイムアウトした場合この段階的なアプローチには2つのメリットがあります。
- 各ステップでレビューできる。型定義が間違っていたら、コンポーネント全体を書き直す必要がない
- AIのコンテキストが累積する。前のステップで定義した型を次のステップで正しく使ってくれる
一度に「ログインフォームを完璧に作って」と言うと、AIは全部を一回で出そうとして、細部が粗くなります。バイクの組み立てと同じで、フレーム → エンジン → 外装の順に組む方が、完成品の精度は高い。
よくある失敗パターンと対策
失敗1:AIの出力をそのまま使う
AIが出したコードをコピー&ペーストしてそのまま使う。これが最も多い失敗です。
AIの出力はあくまで「たたき台」です。特に以下の点は人間が必ず確認する必要があります。
| 確認項目 | よくある問題 | 対策 |
|---|---|---|
| 廃止されたAPI | getServerSideProps(App Routerでは不要) | Next.jsの最新ドキュメントと突き合わせる |
| セキュリティ | ユーザー入力の未サニタイズ | dangerouslySetInnerHTMLの有無を確認 |
| パフォーマンス | 不要なuseEffectやre-render | React DevTools Profilerで確認 |
| 型の安全性 | as any、型アサーション | strict modeでビルドチェック |
失敗2:プロンプトが長すぎる
プロンプトに情報を詰め込みすぎると、AIは重要な要件と補足情報を区別できなくなります。
目安として、1つのプロンプトで依頼するのは1つのコンポーネントまで。ページ全体を一度に依頼するのではなく、コンポーネント単位で分割して依頼する方が品質が上がります。
失敗3:「動くコード」で満足する
AIが出したコードがnpm run devで動いた時点で満足してしまう。しかし、「動く」と「使える」は違います。
私が実案件でAIの出力を受け取った後にチェックする項目は以下の通りです。
tsc --noEmitでTypeScriptの型チェックをパスするか- キーボード操作でフォームを送信できるか(Tab → Enter)
- スクリーンリーダーで意味が通るか(aria属性、label)
- 状態のエッジケース(空入力、長い文字列、ネットワークエラー)
まとめ
今回は、AI駆動開発においてReact/Next.jsで「使えるコード」を引き出すプロンプト設計のTipsを紹介しました。
AIは優秀なジュニアエンジニアのようなものです。明確な要件定義書を渡せば質の高いコードを書いてくれますが、「いい感じに作って」では期待通りのものは出てきません。
プロンプト設計の3つの原則をまとめます。
- 「What」ではなく「Context」を伝える。フレームワーク、データの流れ、既存の設計パターン、ユーザーの操作フローを明示する
- 「制約」を先に伝える。使わないもの、使うもの、APIの最新変更を最初に指定する
- 「段階的に」指示を出す。型定義 → コンポーネント → エッジケース の順に分割して依頼する
AIの出力はたたき台です。最終的な品質を決めるのは、人間のレビューと判断です。ただし、良いたたき台があれば、レビューと修正にかかる時間は大幅に短縮できます。AI駆動開発の真価は、この「たたき台の精度を上げる技術」にあると私は考えています。
株式会社ファストコーディングでは、AI駆動開発を取り入れたフロントエンド実装を行っています。「AI活用で開発効率を上げたい」「React/Next.jsの実装品質を改善したい」という方は、お問い合わせフォームからお気軽にご相談ください。

