Next.js 16 + React 19 + React Hook Form + Zod で「入力チェック付きフォーム
この記事は、Next.js / React / React Hook Form / Zod のバージョンを明記したうえで、
- Zod でバリデーション
- React Hook Form(RHF)でフォーム入力を扱い
- 送信で Server Actions(= React の Server Functions)を動かす
ところまでを、初心者向けに基礎から説明します。
また、次のような「ありがちな疑問・勘違い」を解消します:
-
「Server Actions って
<form action>+useActionStateのことだけ?」 -
「RHF を使うなら
<form action>ではなくonSubmit={handleSubmit(...)}が定石なのはなぜ?」 -
「クライアントで
submitContact()を呼んでいるのに、なぜその先はサーバー処理として実行できるの?」 -
👉 **環境構築手順(コマンド / package.json)**は後半にまとめています:
環境構築手順へ
2026/01時点で、npx create-next-app@latest を
--yesオプション付きで全て、質問はすべてyesで環境作ったところ、
Next.js 16.1.1で構成され、TypeScript / Tailwind CSS / ESLint / App Router / Turbopack が有効になり、
Tailwind を「すぐ使える状態」でプロジェクトが作られます。
所定の位置に、フォルダを切って、ソースコードを配置するだけ( ゼロコンフィグ )で、
動作するため、非常にお手軽です。
Next.jsが流行ってる理由は、このゼロコンフィグでお手軽なところではないか。
検証に使ったバージョン(package-lock.json で固定)
- Next.js: 16.1.1
- React: 19.2.3
- react-dom: 19.2.3
- react-hook-form: 7.69.0
- Zod: 4.3.2
- @hookform/resolvers: 5.2.2
まず「Server Actions」って何?
「Server Actions」という言葉は、<form action> の書き方だけを指すものではありません。
Next.js の公式ドキュメントでは、Server Actions を次のように説明しています(要点):
- サーバーで実行される async 関数
-
"use server"ディレクティブで定義する -
<form action>で呼べる - さらに
<form>に限らず、イベントハンドラやサードパーティライブラリ(RHF など)からも呼べる
(出典:Next.js docs「Server Actions and Mutations」)
そして React 側でも "use server" の関数(Server Functions)について:
- フレームワーク(Next.js など)がサーバー参照を自動生成する
- クライアントから呼ぶと、サーバーへリクエストしてサーバー側で実行し、結果を返す
(出典:React docs「Server Functions」「use server」)
ここまでが「Server Actions(Server Functions)」の定義です。
補足:Server Actions は、React の Server Functions(
"use server") をフレームワーク側が実行可能にした仕組みです。React 本体は「Server Function の参照を作り、クライアント呼び出しをサーバー実行へ変換する」ための統合点を提供し、実際の実装は Next.js などのフレームワーク/バンドラが担います。
Next.js(App Router)以外にも、RSC 対応を進めている React Router 、RSC フレームワークの Waku 、RSC/Server Functions を提供する RedwoodSDK などがあります。
また React 19 は安定版(stable)としてリリースされ、React 19 の React Server Components は「minor バージョン間で破壊しない stable」と説明されています(ただし実装側の低レベル API は semver 対象外の注意も記載)。
<form action> + useActionState は Server Actions の“代表的な使い方”のひとつ
Next.js の公式ガイド(Forms)には、useActionState を使って pending(送信中)やエラー表示を扱い、
<form action={formAction}> で送る例が載っています。
(出典:Next.js docs「Forms」)
つまり、
-
<form action>方式は Server Actions の代表的な呼び出し方 - ただし Server Actions そのものの定義が
<form action>に限定されているわけではない
という整理になります。
ここが本題:なぜ RHF のときに <form action> ではなく onSubmit={handleSubmit(...)} が定石になりやすいのか
よくある疑問:
RHF を使うなら、なぜ
<form action={serverAction}>ではなく、onSubmit={handleSubmit(onSubmit)}がよく使われるの?
重要ポイント:RHF の handleSubmit は “ブラウザの通常 submit” を止める(preventDefault する)
RHF は、フォーム送信を「ブラウザの標準 submit」ではなく、JavaScript 側で主導して扱う設計です。
そのため handleSubmit は内部で event.preventDefault() を呼びます。
この挙動が原因で、フレームワーク側のフォーム送信(例:Remix の <Form>)と衝突する例が、RHF の公式 GitHub Issue に明確に書かれています:
この事実から、次が言えます:
-
<form action={serverAction}>が期待しているのは “通常の submit” - しかし
onSubmit={handleSubmit(...)}を付けると、RHF が 通常の submit を止める - だから
<form action>方式で「submit として Server Action を発火させるルート」は噛み合いにくい
✅ よって、「RHF を使う=
<form action>が絶対不可能」ではありません。
ただし RHF の定石(handleSubmit で送信制御)を取る限り、<form action>方式の“通常 submit 経路”とは両立しにくい、が正確な理解です。
RHF + Server Actions の定石は?
Next.js 公式は「Server Actions は <form> に限らず、イベントハンドラやサードパーティライブラリから呼べる」と明記しています:
なので RHF での定石は次の形になります:
- RHF が
handleSubmitで入力値を集める(+クライアント側バリデーション) -
onSubmitの中で Server Action を普通に呼ぶ
もう一段深い話:「なぜ submitContact() を呼んだ先が“サーバー処理”になれるのか?」
クライアント側コードで submitContact(values) を呼んでいるのに、
なぜその先はサーバー処理として実行できるのでしょうか?
境界は "use server" ディレクティブ
サンプルでは actions.ts の先頭に "use server" が書かれています。
Next.js 公式は "use server" により Server Actions を定義できること、そしてクライアント側が import できる制約などを説明しています。
(出典:Next.js docs「Server Actions and Mutations」)
理屈は「クライアントバンドルには“参照”だけが入り、実体はサーバーで実行」
React 公式は、"use server" の関数が サーバー参照として扱われ、クライアントから呼ぶとサーバーへリクエストして実行されることを説明しています。
(出典:React docs「Server Functions」)
Next.js 公式も、Server Actions は POST で呼ばれ、引数・戻り値にはシリアライズ制約があることを説明しています。
(出典:Next.js docs「Server Actions and Mutations」)
サンプルコード(ファイル単位で全文)と解説
この章では、実際に動く最小構成を ファイル単位で全文掲載し、要点を解説します。
(相対パスはコードブロックの直前に明記します)
app/features/contact/schema.ts
import { z } from "zod";
export const contactSchema = z.object({
name: z.string().min(1, "名前は必須です"),
email: z.email({ message: "メールアドレスの形式が不正です" }),
message: z
.string()
.min(1, "メッセージは必須です")
.max(1000, "1000文字までです"),
});
export type ContactInput = z.infer<typeof contactSchema>;
ポイント
- Zod のスキーマは クライアント/サーバーで共通利用できます。
- 今回は
-
useForm({ resolver: zodResolver(contactSchema) })で クライアント側バリデーション - Server Action 内の
safeParse()で サーバー側バリデーション
という二重構えです(クライアント入力は改ざんできるので、サーバー側検証は必須)。
-
app/features/contact/actions.ts
"use server";
import { contactSchema, type ContactInput } from "./schema";
export type SubmitResult =
| { ok: true; message: string }
| {
ok: false;
message: string;
fieldErrors?: Record<string, string[] | undefined>;
};
export async function submitContact(
input: ContactInput
): Promise<SubmitResult> {
const parsed = contactSchema.safeParse(input);
if (!parsed.success) {
const flattened = parsed.error.flatten();
return {
ok: false,
message: "サーバー側バリデーションに失敗しました",
fieldErrors: flattened.fieldErrors,
};
}
// ここが "サーバーロジック" の場所(DB保存や外部API呼び出し等に置き換える)
console.log("submitContact (server):", parsed.data);
return {
ok: true,
message: "送信しました(Server Actions が実行されました)",
};
}
ポイント
- 先頭の
"use server"が境界です(このファイルの export が Server Action になります)。 -
safeParse()で サーバー側でも必ず検証しています。 - DB 保存や外部 API 呼び出しは、この関数内に置けば「サーバー処理」として実行されます。
const parsed = contactSchema.safeParse(input); は何か?
これは一言でいうと、信頼できない入力データ input を、Zod スキーマ contactSchema で実行時に検証し、成功/失敗を「例外なし」で受け取る処理です。
Zod の .parse() は失敗すると 例外(ZodError)を throwしますが、.safeParse() は 例外を投げずに結果オブジェクトを返します(Zod 公式の説明・サンプルどおり)。
なぜサーバー側で safeParse しなきゃならないの?
理由は1つだけです:サーバーは、クライアントから来たデータを信用してはいけないからです。
React Hook Form や Zod Resolver でクライアント側バリデーションをしていても、ネットワーク経由でサーバーに届く値は改ざんできます。
だからサーバー側でも 必ずスキーマ検証して、正しい入力だけを通します。
input は何か?どんな型・どんな形のオブジェクトか?
このプロジェクトでは、input の型は ContactInput です(schema.ts の定義)。
export type ContactInput = z.infer<typeof contactSchema>;
contactSchema は name / email / message を持つ z.object(...) なので、ContactInput は次の形です。
type ContactInput = {
name: string;
email: string;
message: string;
};
具体例(正しい形の input)はこうです。
const input: ContactInput = {
name: "山田太郎",
email: "taro@example.com",
message: "お問い合わせです。",
};
戻り値 parsed は何か?どんな型・どんな形のオブジェクトか?
Zod 公式 README の safeParse の説明どおり、戻り値は success が true/false で分岐できる形です。
このプロジェクト(transform 等を使っていない)では、概念的には次の形になります。
type SafeParseResult =
| { success: true; data: ContactInput }
| { success: false; error: z.ZodError<ContactInput> };
成功したとき(parsed.success === true)
parsed.data は **検証済みデータ(ContactInput)**です。
if (parsed.success) {
// parsed.data は ContactInput 型
parsed.data.name; // string
parsed.data.email; // string
parsed.data.message; // string
}
失敗したとき(parsed.success === false)
parsed.error は ZodError です。
if (!parsed.success) {
// parsed.error は ZodError
parsed.error;
}
parsed.error.flatten() は何を返すの?
Zod の公式ドキュメント(Error formatting)どおり、flatten() は次の形のオブジェクトを返します。
-
formErrors: string[](全体エラー) -
fieldErrors: Record<string, string[]>(フィールドごとのエラー配列) - https://zod.dev/error-formatting
const flattened = parsed.error.flatten();
/*
flattened = {
formErrors: string[],
fieldErrors: {
[fieldName: string]: string[]
}
}
*/
まとめ(この1行がやってること)
const parsed = contactSchema.safeParse(input);
この1行は、サーバーに届いた input をスキーマで検証し、
- ✅ 正しければ
success: trueでdataに「検証済みデータ」 - ❌ 間違っていれば
success: falseでerrorに「エラー詳細」
を返す “サーバー側の安全装置” です。
app/features/contact/contact-form.tsx
"use client";
import { useState, useTransition } from "react";
import { useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import { contactSchema, type ContactInput } from "./schema";
import { submitContact, type SubmitResult } from "./actions";
export default function ContactForm() {
const [result, setResult] = useState<SubmitResult | null>(null);
const [isPending, startTransition] = useTransition();
const {
register,
handleSubmit,
reset,
formState: { errors },
} = useForm<ContactInput>({
resolver: zodResolver(contactSchema),
defaultValues: { name: "", email: "", message: "" },
mode: "onBlur",
});
const onSubmit = (values: ContactInput) => {
setResult(null);
startTransition(async () => {
const r = await submitContact(values);
setResult(r);
if (r.ok) reset();
});
};
return (
<div className="w-full max-w-2xl mx-auto">
<form
onSubmit={handleSubmit(onSubmit)}
className="space-y-6 bg-white dark:bg-gray-800 rounded-2xl shadow-xl p-8 border border-gray-200 dark:border-gray-700"
>
<div className="space-y-2">
<label
htmlFor="name"
className="block text-sm font-semibold text-gray-700 dark:text-gray-300"
>
名前
</label>
<input
id="name"
{...register("name")}
className="w-full px-4 py-3 rounded-lg border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100 placeholder-gray-400 dark:placeholder-gray-500 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent transition-all duration-200"
placeholder="お名前を入力してください"
/>
{errors.name && (
<p className="text-sm text-red-500 dark:text-red-400 mt-1 flex items-center gap-1">
<span>⚠️</span>
{errors.name.message}
</p>
)}
</div>
<div className="space-y-2">
<label
htmlFor="email"
className="block text-sm font-semibold text-gray-700 dark:text-gray-300"
>
メールアドレス
</label>
<input
id="email"
type="email"
{...register("email")}
className="w-full px-4 py-3 rounded-lg border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100 placeholder-gray-400 dark:placeholder-gray-500 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent transition-all duration-200"
placeholder="example@email.com"
/>
{errors.email && (
<p className="text-sm text-red-500 dark:text-red-400 mt-1 flex items-center gap-1">
<span>⚠️</span>
{errors.email.message}
</p>
)}
</div>
<div className="space-y-2">
<label
htmlFor="message"
className="block text-sm font-semibold text-gray-700 dark:text-gray-300"
>
メッセージ
</label>
<textarea
id="message"
{...register("message")}
rows={6}
className="w-full px-4 py-3 rounded-lg border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100 placeholder-gray-400 dark:placeholder-gray-500 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent transition-all duration-200 resize-none"
placeholder="メッセージを入力してください"
/>
{errors.message && (
<p className="text-sm text-red-500 dark:text-red-400 mt-1 flex items-center gap-1">
<span>⚠️</span>
{errors.message.message}
</p>
)}
</div>
<button
type="submit"
disabled={isPending}
className="w-full py-3 px-6 bg-linear-to-r from-blue-600 to-blue-700 hover:from-blue-700 hover:to-blue-800 text-white font-semibold rounded-lg shadow-lg hover:shadow-xl transform hover:-translate-y-0.5 transition-all duration-200 disabled:opacity-50 disabled:cursor-not-allowed disabled:transform-none disabled:shadow-lg"
>
{isPending ? (
<span className="flex items-center justify-center gap-2">
<svg
className="animate-spin h-5 w-5"
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
>
<circle
className="opacity-25"
cx="12"
cy="12"
r="10"
stroke="currentColor"
strokeWidth="4"
></circle>
<path
className="opacity-75"
fill="currentColor"
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
></path>
</svg>
送信中...
</span>
) : (
"送信する"
)}
</button>
{result && (
<div
className={`mt-6 p-4 rounded-lg border-2 ${
result.ok
? "bg-green-50 dark:bg-green-900/20 border-green-200 dark:border-green-800"
: "bg-red-50 dark:bg-red-900/20 border-red-200 dark:border-red-800"
} transition-all duration-300 animate-in fade-in slide-in-from-top-2`}
>
<p
className={`text-sm font-medium flex items-center gap-2 ${
result.ok
? "text-green-800 dark:text-green-300"
: "text-red-800 dark:text-red-300"
}`}
>
<span className="text-lg">{result.ok ? "✅" : "❌"}</span>
{result.message}
</p>
{!result.ok && result.fieldErrors && (
<div className="mt-3 p-3 bg-white dark:bg-gray-800 rounded border border-gray-200 dark:border-gray-700">
<pre className="text-xs text-gray-600 dark:text-gray-400 whitespace-pre-wrap overflow-x-auto">
{JSON.stringify(result.fieldErrors, null, 2)}
</pre>
</div>
)}
</div>
)}
</form>
</div>
);
}
ポイント(なぜ onSubmit={handleSubmit(onSubmit)} なのか)
- RHF は
handleSubmitによって送信を JS 主導にする設計です。 - そして Next.js 公式は、Server Actions が
<form>だけでなく イベントハンドラやサードパーティライブラリから呼べると明記しています。
そのため RHF のonSubmitの中でsubmitContact(values)を呼ぶ形が成立します。
ポイント(startTransition の役割)
-
useTransition/startTransitionは、送信中の UI(ボタン無効化など)を素直に書けるようにするために使えます。
React 公式の Server Functions の説明では、useTransitionを併用する例があります。
なぜ「ここで」startTransition を使う必要があったのか?(使わないとどうなる?)
結論:このサンプルは「送信中の状態(pending)を正しく取り出して、送信中UIを確実に出す」ために useTransition / startTransition を使っています。
送信(Server Function 呼び出し)自体は startTransition がなくても実行できますが、UI 側が不親切になります。
1) startTransition を使わないと、isPending が取れない(=送信中UIを出しにくい)
React の Server Functions の説明では、Server Function を自分で呼ぶ場合に
useTransition と startTransition で包むことで pending 状態にアクセスできる例が示されています。
また、useTransition() は [isPending, startTransition] を返すと明記されています。
つまり、送信中にボタンを無効化する/「送信中…」を表示するのに isPending が必要で、
isPending を得るために startTransition を使っています。
2) startTransition を使わないと、送信結果の setState が “urgent(通常優先)更新” になる
React は Transition にした更新を **non-blocking(UI をブロックしない)**扱いにします。
逆に言えば、Transition にしない更新は 通常(urgent)更新として扱われます。
このフォームでは、送信後に setResult(r) などで UI を更新します。
それを Transition にしておくと、React は「入力・描画の応答性」を保ちつつ更新を進められます。
3) 重要:await の後の setState は Transition 扱いにならない(公式の既知制約)
公式は await 後の state 更新は Transition として扱われないので、
必要なら 追加で startTransition で包めと明記しています。
まとめ(初心者向けに一言で)
-
startTransitionは 「送信中(pending)を UI に出す」ために使う - そして 送信後の UI 更新を non-blocking にするために使う
-
startTransitionを外すと、送信はできても 送信中UIが出せない/UI更新が通常優先になるので、体験が悪くなる
app/contact/page.tsx
import ContactForm from "../features/contact/contact-form";
export default function ContactPage() {
return (
<main className="min-h-screen bg-linear-to-br from-gray-50 via-blue-50 to-indigo-50 dark:from-gray-900 dark:via-gray-800 dark:to-gray-900 py-12 px-4 sm:px-6 lg:px-8">
<div className="max-w-4xl mx-auto">
<div className="text-center mb-12">
<h1 className="text-4xl sm:text-5xl font-bold dark:text-white mb-4 bg-linear-to-r from-blue-600 to-indigo-600 bg-clip-text text-transparent">
お問い合わせ
</h1>
<p className="text-lg text-gray-600 dark:text-gray-400 max-w-2xl mx-auto">
ご質問やご意見がございましたら、お気軽にお問い合わせください。
できる限り早くご返信いたします。
</p>
</div>
<ContactForm />
</div>
</main>
);
}
ポイント
- ルーティング(
/contact)に対応するページ。フォーム本体はfeatures/contact側に切り出し、ページ側は「配置して表示するだけ」にして見通しを良くしています。
app/layout.tsx
import type { Metadata } from "next";
import { Geist, Geist_Mono } from "next/font/google";
import "./globals.css";
const geistSans = Geist({
variable: "--font-geist-sans",
subsets: ["latin"],
});
const geistMono = Geist_Mono({
variable: "--font-geist-mono",
subsets: ["latin"],
});
export const metadata: Metadata = {
title: "Create Next App",
description: "Generated by create next app",
};
export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
return (
<html lang="en">
<body
className={`${geistSans.variable} ${geistMono.variable} antialiased`}
>
{children}
</body>
</html>
);
}
ポイント
- App Router のレイアウト。全ページ共通の骨組み(
<html>,<body>)や、フォント/グローバル CSS をここで適用します。
app/page.tsx
import Link from "next/link";
export default function Page() {
return (
<main className="min-h-screen bg-linear-to-br from-gray-50 via-blue-50 to-indigo-50 dark:from-gray-900 dark:via-gray-800 dark:to-gray-900 flex items-center justify-center px-4">
<div className="text-center">
<h1 className="text-4xl sm:text-5xl font-bold text-gray-900 dark:text-white mb-8">
ようこそ
</h1>
<Link
href="/contact"
className="inline-block px-8 py-4 bg-linear-to-r from-blue-600 to-indigo-600 hover:from-blue-700 hover:to-indigo-700 text-white font-semibold rounded-lg shadow-lg hover:shadow-xl transform hover:-translate-y-0.5 transition-all duration-200"
>
お問い合わせはこちら
</Link>
</div>
</main>
);
}
ポイント
- トップページ(
/)。このサンプルでは/contactへ誘導する入口として最小構成にしています。
環境構築手順
このサンプルは、以下の手順で作成して npm run dev で動作確認できます。
1. Next.js プロジェクトの作成
npx create-next-app@latest next16-rhf-zod-actions --yes
補足:
next16-rhf-zod-actions は 作成されるプロジェクト(フォルダ)名です。
--yesを付けると、create-next-app の質問プロンプトをスキップし、保存済みの設定(なければ推奨デフォルト)で雛形を生成します。推奨デフォルトでは TypeScript / Tailwind CSS / ESLint / App Router / Turbopack が有効になり、Tailwind を「すぐ使える状態」でプロジェクトが作られます。
なお「Tailwind がデフォルトに含まれる」ことは、少なくとも Next.js 13 の公式ドキュメント上でcreate-next-app --helpの--tailwindに “(default)” と明記されています。
2. Next.js、React、React DOM のインストール
npm i next@16.1.1 react@19.2.3 react-dom@19.2.3
2026/01時点の最新バージョン(next 16.1.1、react 19.2.3、react-dom 19.2.3)を固定してインストールしています。
もし、インストール時点の latest(dist-tag) を使って入れたいならば、npm i next@latest react@latest react-dom@latest(または npm i next react react-dom)のコマンドラインになります
3. 必要なライブラリのインストール
npm i react-hook-form@7.69.0 zod@4.3.2 @hookform/resolvers@5.2.2
2026/01時点の最新バージョン(react-hook-form 7.69.0、zod 4.3.2、@hookform/resolvers 5.2.2)を固定してインストールしています。
もし、インストール時点の latest(dist-tag) を使って入れたいならば、npm i react-hook-form@latest zod@latest @hookform/resolvers@latest(または npm i react-hook-form zod @hookform/resolvers)のコマンドラインになります
4. 起動
npm run dev
package.json
2026/01の上記の環境構築方法で、動作確認した時点のpackage.jsonは、下記のとおり
{
"name": "next16-rhf-zod-actions",
"private": true,
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "eslint"
},
"dependencies": {
"@hookform/resolvers": "^5.2.2",
"next": "^16.1.1",
"react": "^19.2.3",
"react-dom": "^19.2.3",
"react-hook-form": "^7.69.0",
"zod": "^4.3.2"
},
"devDependencies": {
"@tailwindcss/postcss": "^4",
"@types/node": "^20",
"@types/react": "^19",
"@types/react-dom": "^19",
"eslint": "^9",
"eslint-config-next": "16.1.1",
"tailwindcss": "^4",
"typescript": "^5"
}
}
まとめ
-
Server Actions は
<form action>専用ではない("use server"の async 関数をサーバーで実行する仕組み)。 -
RHF の定石は
onSubmit={handleSubmit(...)}:handleSubmitが通常 submit を止めて JS 主導にする設計だから。 -
submitContact(values)がサーバー処理になれる境界は"use server":クライアント側は参照を呼び、実体はサーバーで実行される。
参考(公式)
- Next.js: Server Actions and Mutations
https://nextjs.org/docs/app/building-your-application/data-fetching/server-actions-and-mutations - Next.js: Forms guide
https://nextjs.org/docs/app/guides/forms - Next.js:
"use server"directive
https://nextjs.org/docs/app/api-reference/directives/use-server - React: Server Functions
https://react.dev/reference/rsc/server-functions - React:
'use server'
https://react.dev/reference/rsc/use-server - RHF:
handleSubmitがpreventDefault()することが問題になる例(RHF公式GitHub Issue)
https://github.com/react-hook-form/react-hook-form/issues/9774
Discussion