Showing formState errors in React Hook Form
formState.errors holds an entry for each field that failed validation, which you can render next to the input or collect into a summary.
All React Hook Form examples React Hook Form: usage and stats
How it works
- Get errors from useForm.
errorscomes fromformState, as in the Quickstart. The component reads it on every render to update the messages and the summary panel. [1] - Give every rule a message. The Quickstart checks
lastNameandageand writes the messages by hand. Here the same texts are passed in the rules, so each entry inerrorscarries its ownmessage. [1] - List all errors.
Object.entries(errors)turns the errors into a list of field names and messages, which the right panel prints. The panel turns green again once every field passes. [2]
Run and edit it
It runs in your browser, in a sandboxed frame that cannot see this site; the packages load from esm.sh only after you press Run.
import { useForm } from "react-hook-form";
const S = {
page: { minHeight: "100vh", display: "flex", alignItems: "center", justifyContent: "center", gap: 16, background: "#111", color: "#e8eaf0", fontFamily: "system-ui, sans-serif" },
card: { background: "#1c1f26", padding: 18, borderRadius: 12, width: 250, boxShadow: "0 8px 24px rgba(0,0,0,.45)" },
h: { margin: "0 0 10px", fontSize: 17 },
label: { display: "block", fontSize: 13, color: "#a9b0c0" },
input: { display: "block", width: "100%", boxSizing: "border-box", padding: "7px 9px", marginTop: 4, borderRadius: 6, border: "1px solid #3a3f4b", background: "#0f1115", color: "#e8eaf0", fontSize: 14 },
err: { color: "#ff7b7b", fontSize: 12, margin: "3px 0 8px", minHeight: 15 },
btn: { marginTop: 6, width: "100%", padding: "9px 0", border: 0, borderRadius: 6, background: "#6c8cff", color: "#fff", fontWeight: 600, fontSize: 14, cursor: "pointer" },
out: { background: "#1c1f26", padding: 18, borderRadius: 12, width: 220, minHeight: 120, fontFamily: "ui-monospace, monospace", fontSize: 13, whiteSpace: "pre-wrap", color: "#9fe6a0" },
};
export default function App() {
const { register, handleSubmit, formState: { errors } } = useForm();
const list = Object.entries(errors); // one entry per field that failed
return (
<div style={S.page}>
<form onSubmit={handleSubmit(() => {})} style={S.card}>
<h3 style={S.h}>Profile</h3>
<label style={S.label}>First name
<input {...register("firstName", { required: "First name is required." })} style={S.input} />
</label>
<div style={S.err}>{errors.firstName && errors.firstName.message}</div>
<label style={S.label}>Last name
<input {...register("lastName", { required: "Last name is required." })} style={S.input} />
</label>
<div style={S.err}>{errors.lastName && errors.lastName.message}</div>
<label style={S.label}>Age
<input
placeholder="try: twelve"
style={S.input}
{...register("age", { pattern: { value: /^\d+$/, message: "Please enter a number for age." } })}
/>
</label>
<div style={S.err}>{errors.age && errors.age.message}</div>
<button type="submit" style={S.btn}>Check</button>
</form>
<div style={{ ...S.out, color: list.length ? "#ffb4b4" : "#9fe6a0" }}>
<div style={{ color: "#a9b0c0", marginBottom: 6 }}>formState.errors</div>
{list.length
? list.map(([name, e]) => "- " + name + ": " + e.message).join("\n")
: "No errors. Press Check with empty fields."}
</div>
</div>
);
}
Press Check with empty fields, then type a word into Age: the summary on the right lists each failing field and its message. Code written by AgentGid [1].
Press Check with empty fields, then type a word into Age: the summary on the right lists each failing field and its message. Code written by AgentGid [1].Checked Oct 12, 2026: it starts without errors in Chromium with react-hook-form 7.90.0, react 19.3.0, react-dom 19.3.0. Needs WebGL.
More React Hook Form examples
useForm, register and handleSubmit in React Hook Form Required fields in React Hook Form Email validation with pattern in React Hook Form Custom validation with validate in React Hook Form
Sources
Explanations written by AgentGid from the official docs; the numbers in brackets point here. The code marked "written by AgentGid" is ours and free to copy.