Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
e9499f9
Fix watch overload types (#1159)
Ovoda Jul 10, 2025
35291ad
Clarify structure of nested errors in a custom resolver (#1160)
DLiblik Jul 15, 2025
eb75a10
fix: correct typo issue for link formstate and formState type example…
candymask0712 Jul 18, 2025
57ac298
Update sponsorsList.tsx (#1163)
bluebill1049 Jul 20, 2025
bff10cf
add button
bluebill1049 Jul 20, 2025
51a861b
feat: useWatch compute prop (#1145)
bluebill1049 Jul 24, 2025
711e8de
fixed type in useForm (#1164)
Jul 24, 2025
aa307e4
chore(deps): bump next from 15.1.2 to 15.2.4 (#1144)
dependabot[bot] Jul 24, 2025
23e1b42
Merge remote-tracking branch 'upstream/master'
minchodang Aug 6, 2025
d6e76a7
fix #1165
bluebill1049 Aug 10, 2025
bdcceec
Add docs for new lenses methods (#1167)
aspirisen Aug 14, 2025
7415646
update ads location (#1168)
bluebill1049 Aug 16, 2025
eeb9c03
app deps upgrade
bluebill1049 Aug 16, 2025
2332075
chore: app dep upgrade
bluebill1049 Sep 6, 2025
c1d7ad3
update format
bluebill1049 Sep 6, 2025
f98461e
update get values api
bluebill1049 Sep 19, 2025
7734b86
Update errormessage.mdx (#1170)
N3M0dropserver Sep 20, 2025
98133e6
udpate sponsors
bluebill1049 Oct 4, 2025
e32f239
feat: update doc on Watch component (#1171)
bluebill1049 Oct 10, 2025
f6a038d
fix: Corrects broken legacy API links in version switcher (v6/v5) (#1…
ria-ahyoung Oct 26, 2025
033aa2f
docs: fix typos and syntax errors in FAQ documentation (#1176)
wo-o29 Oct 29, 2025
521e746
docs: fix code examples in advanced-usage.mdx (#1177)
wo-o29 Oct 29, 2025
a8fad1a
docs: fix incorrect code examples in TypeScript support documentation…
wo-o29 Oct 29, 2025
42f73cd
Merge remote-tracking branch 'upstream/master' into sync-upstream-202…
minchodang Nov 2, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions next-env.d.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
/// <reference types="next" />
/// <reference types="next/image-types/global" />
/// <reference path="./.next/types/routes.d.ts" />

// NOTE: This file should not be edited
// see https://nextjs.org/docs/pages/api-reference/config/typescript for more information.
6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
"clsx": "^1.2.1",
"contentlayer": "^0.3.4",
"little-state-machine": "^4.8.1",
"next": "^15.1.2",
"next": "^15.5.2",
"next-contentlayer": "^0.3.4",
"next-themes": "^0.2.1",
"prism-react-renderer": "^2.4.1",
Expand Down Expand Up @@ -46,8 +46,8 @@
"eslint-plugin-react-hooks": "5.1.0",
"husky": "^8.0.3",
"lint-staged": "^13.3.0",
"prettier": "^3.5.3",
"typescript": "^5.8.3",
"prettier": "^3.6.2",
"typescript": "^5.9.2",
"typescript-eslint": "8.18.1"
},
"keywords": [
Expand Down
1,419 changes: 741 additions & 678 deletions pnpm-lock.yaml

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion src/components/ApiGallery.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ export default function ApiGallery() {

if (version !== 7) {
router.push(
`https://react-hook-form-website-git-leagcy-hook-form.vercel.app/${version}/api`
`https://react-hook-form-website-git-leagcy-hook-form.vercel.app/v${version}/api`
)
} else {
router.push(`/v${version}/docs/`)
Expand Down
38 changes: 38 additions & 0 deletions src/components/CarbonAds.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
"use client"
import { useEffect, useRef } from "react"

export const CARBON_SCRIPT_ID = "_carbonads_js"
const CARBON_SCRIPT_SRC =
"https://cdn.carbonads.com/carbon.js?serve=CW7DTKQ7&placement=react-hook-formcom&format=cover"

export function CarbonAds({ id }: { id: string }) {
const containerRef = useRef<HTMLDivElement>(null)

useEffect(() => {
function injectScript() {
if (document.getElementById(id)) return

const script = document.createElement("script")
script.id = id
script.async = true
script.src = CARBON_SCRIPT_SRC
script.type = "text/javascript"

if (containerRef.current) {
containerRef.current.appendChild(script)
}
}

if (document.readyState === "complete") {
injectScript()
} else {
window.addEventListener("load", injectScript)

return () => window.removeEventListener("load", injectScript)
}

return
}, [id])

return <div className="carbonAdsContainer" ref={containerRef} />
}
6 changes: 4 additions & 2 deletions src/components/Menu/Menu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import styles from "./SideMenu.module.css"
import typographyStyles from "../../styles/typography.module.css"
import { useRouter } from "next/router"
import { Pages } from "../../types/types"
import { CARBON_SCRIPT_ID, CarbonAds } from "@/components/CarbonAds"

function Menu({ pages = [] }: { pages: Pages }) {
const router = useRouter()
Expand Down Expand Up @@ -70,8 +71,9 @@ function Menu({ pages = [] }: { pages: Pages }) {
)
})}
</ul>

<div id="carbon-cover" />
<div className={styles.ads}>
<CarbonAds id={CARBON_SCRIPT_ID} />
</div>
</div>
</aside>
)
Expand Down
6 changes: 6 additions & 0 deletions src/components/Menu/MenuLinks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,12 @@ export const apiLinks: Pages = [
{
pathname: "/docs/usewatch",
name: "useWatch",
pages: [
{
pathname: "/docs/usewatch/watch",
name: "Watch",
},
],
},
{
pathname: "/docs/useformstate",
Expand Down
12 changes: 11 additions & 1 deletion src/components/Menu/SideMenu.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@
max-width: 230px;
padding: 0;
overflow-y: auto;
height: calc(100vh - 190px);
height: calc(100vh - 600px);
overflow-y: auto;
}

Expand All @@ -49,6 +49,10 @@
display: flex;
}

.menu > div > ul > li:last-child {
padding-bottom: 0;
}

.menu > div > ul > li > a {
text-decoration: none;
padding-left: 6px;
Expand Down Expand Up @@ -118,6 +122,7 @@
.menu > div > ul {
margin-top: 0;
max-width: 260px;
height: calc(100vh - 450px);
}

.menu > ul {
Expand Down Expand Up @@ -204,3 +209,8 @@
.menu ul li a.isActive {
border-bottom: 1px solid var(--color-secondary);
}

.ads {
position: absolute;
height: 500px;
}
13 changes: 0 additions & 13 deletions src/components/Nav.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -303,19 +303,6 @@ export default function Nav() {
{nav.tools.formBuilder}
</Link>

<a
href="https://www.beekai.com/features/form-builder"
target="_blank"
rel="noopener noreferrer"
className={styles.linkExternal}
>
<b>BEEKAI</b> Form Builder{" "}
<img
src="/images/open-link.svg"
alt="BEEKAI Form Builder"
/>
</a>

<Link
className={router.pathname == "/dev-tools" ? "active" : ""}
href="/dev-tools"
Expand Down
8 changes: 0 additions & 8 deletions src/components/layout.css
Original file line number Diff line number Diff line change
Expand Up @@ -945,11 +945,3 @@ pre[class*="language-"] {
height: auto;
}
}

#carbon-responsive {
margin: 0 auto 50px;
}

#carbon-cover {
margin: 0 auto 50px;
}
13 changes: 0 additions & 13 deletions src/components/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,19 +28,6 @@ const Layout = ({ children }: { children: ReactNode }) => {

return (
<>
{/*<div className="banner">*/}
{/* <p>*/}
{/* Next-gen <strong>form building</strong> platform with code generation.*/}
{/* <a*/}
{/* href="https://beekai.com/"*/}
{/* title="Learn more about BEEKAI form builder"*/}
{/* target="_blank"*/}
{/* rel="noreferrer"*/}
{/* >*/}
{/* Find out more*/}
{/* </a>*/}
{/* </p>*/}
{/*</div>*/}
<a className="skip-main" href="#main">
Skip to content
</a>
Expand Down
6 changes: 6 additions & 0 deletions src/components/sponsorsList.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,12 @@
border-radius: 50px;
}

.add {
border: 1px dashed #ccc;
border-radius: 50px;
font-size: 12px;
}

@media (min-width: 768px) {
.logoGroup {
display: grid;
Expand Down
26 changes: 3 additions & 23 deletions src/components/sponsorsList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,23 +6,6 @@ export function SponsorsList() {
<p className={styles.heading}>SUPPORTED AND BACKED BY</p>

<div className={styles.logoGroup}>
<a
href="https://www.beekai.com/"
target="_blank"
rel="noopener noreferrer"
>
<img
src="https://www.beekai.com/marketing/logo/horizontal.svg"
alt="BEEKAI Form builder"
/>
</a>
<a
href="https://www.route4me.com/"
target="_blank"
rel="noopener noreferrer"
>
<img src="/images/route4me.png" alt="route4me" />
</a>
<a
href="https://www.follower24.de/"
target="_blank"
Expand All @@ -35,15 +18,12 @@ export function SponsorsList() {
/>
</a>
<a
href="https://www.sanity.io//"
href="https://opencollective.com/react-hook-form"
target="_blank"
className={styles.add}
rel="noopener noreferrer"
>
<img
className={styles.twicsy}
src="/images/sanity.png"
alt="sanity io"
/>
+ Sponsor
</a>
</div>
</div>
Expand Down
11 changes: 8 additions & 3 deletions src/content/advanced-usage.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -381,7 +381,7 @@ function App() {

<Input {...register("input")} />

<button type="button" onClick={() => reset({ defaultValues })}>
<button type="button" onClick={() => reset({ ...defaultValues })}>
Reset
</button>
<input type="submit" />
Expand All @@ -408,7 +408,7 @@ function App() {
const onSubmit = (data) => console.log(data)

useEffect(() => {
register({ name: "select" })
register("select")
}, [register])

const handleChange = (e) => setValue("select", e.target.value)
Expand Down Expand Up @@ -521,7 +521,12 @@ const items = Array.from(Array(1000).keys()).map((i) => ({
const WindowedRow = memo(({ index, style, data }) => {
const { register } = useFormContext()

return <input {...register(`${index}.quantity`)} />
return (
<div style={style}>
<label>{data[index].title}</label>
<input {...register(`${index}.quantity`)} />
</div>
)
})

export const App = () => {
Expand Down
48 changes: 28 additions & 20 deletions src/content/docs/useform.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -287,7 +287,6 @@ By default, an input value will be retained when input is removed. However, you
- By default, `shouldUnregister: false` means unmounted fields are **not validated** by built-in validation.
- By setting `shouldUnregister` to true at `useForm` level, `defaultValues` will **not** be merged against submission result.
- Setting `shouldUnregister: true` makes your form behave more closely to native forms.

- Form values are stored within the inputs themselves.
- Unmounting an input removes its value.
- Hidden inputs should use the `hidden` attribute for storing hidden data.
Expand Down Expand Up @@ -329,7 +328,7 @@ This config will enable [browser native validation](https://developer.mozilla.or

- Only works with `onSubmit` and `onChange` modes, as the `reportValidity` execution will focus the error input.
- Each registered field's validation message is required to be string to display them natively.
- This feature only works with the `register` API and `useController/Controller` that are connected with actual DOM references.
- This feature only works with the `register` API and&nbsp; `useController/Controller` that are connected with actual DOM references.

**Examples:**

Expand Down Expand Up @@ -435,7 +434,10 @@ npm install @hookform/resolvers
- A resolver can not be used with the built-in validators (e.g.: required, min, etc.)
- When building a custom resolver:
- Make sure that you return an object with both `values` and `errors` properties. Their default values should be an empty object. For example: `{}`.
- The keys of the `error` object should match the `name` values of your fields.
- The keys of the `errors` object should match the `name` values of your fields, but they _must_ be hierarchical rather than a single key for deep errors:
`❌ { "participants.1.name": someErr }` will not set or clear properly - instead, use `✅ { participants: [null, { name: someErr } ] }` as this is reachable
as `errors.participants[1].name` - you can still prepare your errors using flat keys, and then use a function like this one from the zod resolver:
[toNestErrors(flatErrs, resolverOptions)](https://github.com/react-hook-form/resolvers/blob/master/src/toNestErrors.ts)

</Admonition>

Expand Down Expand Up @@ -491,10 +493,12 @@ const App = () => {
})

return (
<form onSubmit={handleSubmit((data) => {
// handle inputs
console.log(data);
})}>
<form
onSubmit={handleSubmit((data) => {
// handle inputs
console.log(data)
})}
>
<input {...register("name")} />
<input {...register("age", { valueAsNumber: true })} type="number" />
<input type="submit" />
Expand All @@ -504,35 +508,39 @@ const App = () => {
```

```tsx copy sandbox="https://codesandbox.io/s/react-hook-form-joiresolver-v6-ts-forked-5pseh"
import { useForm } from "react-hook-form";
import { joiResolver } from "@hookform/resolvers/joi";
import Joi from "joi";
import { useForm } from "react-hook-form"
import { joiResolver } from "@hookform/resolvers/joi"
import Joi from "joi"

interface IFormInput {
name: string;
age: number;
name: string
age: number
}

const schema = Joi.object({
name: Joi.string().required(),
age: Joi.number().required()
});
age: Joi.number().required(),
})

const App = () => {
const { register, handleSubmit, formState: { errors } } = useForm<IFormInput>({
resolver: joiResolver(schema)
});
const {
register,
handleSubmit,
formState: { errors },
} = useForm<IFormInput>({
resolver: joiResolver(schema),
})
const onSubmit = (data: IFormInput) => {
console.log(data);
};
console.log(data)
}

return (
<form onSubmit={handleSubmit(onSubmit)}>
<input {...register("name")} />
<input type="number" {...register("age")} />
<input type="submit" />
</form>
);
)
}
```

Expand Down
2 changes: 1 addition & 1 deletion src/content/docs/useform/control.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ function App() {
return (
<form onSubmit={handleSubmit(onSubmit)}>
<Controller
as={TextField}
render={({ field }) => <input {...field} />}
name="firstName"
control={control}
defaultValue=""
Expand Down
Loading