Skip to content
Agents tracked: 440 Downloads (7d): 202M down 19.5% GitHub stars: 10.4M Releases (7d): 607 Agent status: 3 with issuesSince last limit reset: Claude 28d · Codex 0d Updated Oct 21, 2026

Custom validation with validate in React Hook Form

When built-in rules are not enough, the validate option runs your own function on the field value and uses the string it returns as the error message.

All React Hook Form examples React Hook Form: usage and stats

Custom validation with validate in React Hook Form: screenshot of the example below running
The code below, running in Chromium on our server (Oct 12, 2026). Press Run to see it live and change it.

How it works

  1. Write the rule as a function. validate takes a function of the field value. The React Hook Form site uses value !== "admin" || "Nice try!": the comparison passes for other names, and the string is returned for admin. [1]
  2. Show the returned string. The string from validate is read back from errors.username.message, the same way the docs example renders it under the input. [1]
  3. Reuse the pattern. The age field uses the same shape with a numeric check, so each field can carry its own rule and message. [1]

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 { useState } from "react";
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 [result, setResult] = useState(null);

  return (
    <div style={S.page}>
      <form onSubmit={handleSubmit((data) => setResult(data))} style={S.card}>
        <h3 style={S.h}>Create account</h3>
        <label style={S.label}>Username
          {/* return true to pass, or a string to show as the error */}
          <input
            placeholder="try: admin"
            style={S.input}
            {...register("username", { validate: (value) => value !== "admin" || "Nice try!" })}
          />
        </label>
        <div style={S.err}>{errors.username && errors.username.message}</div>
        <label style={S.label}>Age
          <input
            placeholder="try: 15"
            style={S.input}
            {...register("age", { validate: (value) => Number(value) >= 18 || "You must be 18 or older" })}
          />
        </label>
        <div style={S.err}>{errors.age && errors.age.message}</div>
        <button type="submit" style={S.btn}>Create</button>
      </form>
      <div style={S.out}>
        {result ? "Valid:\n" + JSON.stringify(result, null, 2) : "Type admin or an age under 18, then press Create"}
      </div>
    </div>
  );
}
Ctrl/⌘ + Enter runs

Free for signed-in members, with a daily limit. Describe the change in plain words; the new code runs only in your browser.

Type admin as the username or an age under 18 and press Create to see the custom messages. 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 Showing formState errors in React Hook Form

Sources

  1. React Hook Form website

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.

Building something with React Hook Form? Ask AgentGid →