Add preact integration for tanstack form#2043
Add preact integration for tanstack form#2043JoviDeCroock wants to merge 14 commits intoTanStack:mainfrom
Conversation
|
|
| Command | Status | Duration | Result |
|---|---|---|---|
nx affected --targets=test:sherif,test:knip,tes... |
❌ Failed | 45s | View ↗ |
nx run-many --target=build --exclude=examples/** |
✅ Succeeded | 7s | View ↗ |
☁️ Nx Cloud last updated this comment at 2026-04-01 00:50:09 UTC
theVedanta
left a comment
There was a problem hiding this comment.
compat and use-client are the only things I wanna flag, since they're in multiple files. Apart from that, looks okay. The onInput prop isn't important as such, but would be a nice-to-have.
| Field: FieldComponent< | ||
| TFormData, | ||
| TOnMount, | ||
| TOnChange, |
There was a problem hiding this comment.
I feel like fields should have an onInput prop instead of onChange for it to be consistent with Preact. If we can do that, it'd be awesome
There was a problem hiding this comment.
I don't think anyone would benefit from that, the end-user is still free to use onInput in exchange for onChange at their disposal
There was a problem hiding this comment.
Oh, gotcha. But how would users use onInput in parts of the API that are not the children?
<form.Field
name="firstName"
validators={{
onInput: ({ value }) =>
!value
? 'A first name is required'
: value.length < 3
? 'First name must be at least 3 characters'
: undefined,
}}
...I can understand if you feel like they don't, I was only thinking about it because of consistency with Preact's naming.
There was a problem hiding this comment.
Ah I see what you mean now, sorry I didn't derive the validator enforcement from the comment
There was a problem hiding this comment.
Validators should use onChange even on adapters that use onInput. That comes from core. No need to change :)
theVedanta
left a comment
There was a problem hiding this comment.
Once we can finalize this, let's get the docs in place and merge! Super excited to use preact-form 🔥
| Field: FieldComponent< | ||
| TFormData, | ||
| TOnMount, | ||
| TOnChange, |
There was a problem hiding this comment.
Oh, gotcha. But how would users use onInput in parts of the API that are not the children?
<form.Field
name="firstName"
validators={{
onInput: ({ value }) =>
!value
? 'A first name is required'
: value.length < 3
? 'First name must be at least 3 characters'
: undefined,
}}
...I can understand if you feel like they don't, I was only thinking about it because of consistency with Preact's naming.
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #2043 +/- ##
==========================================
- Coverage 90.35% 90.31% -0.04%
==========================================
Files 38 55 +17
Lines 1752 2148 +396
Branches 444 522 +78
==========================================
+ Hits 1583 1940 +357
- Misses 149 186 +37
- Partials 20 22 +2 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
|
LGTM! @crutchcorn just needs to approve the worflows perhaps? |
crutchcorn
left a comment
There was a problem hiding this comment.
Pushed some minor changes to sync with main. Otherwise LGTM. Waiting on @KevinVandy to help us publish before merging (he'll merge for us)
|
Looks like the lock has some conflicts now |
36749f4 to
89b831c
Compare
📝 WalkthroughWalkthroughAdds first-class Preact support for TanStack Form: a new Changes
Sequence Diagram(s)sequenceDiagram
participant User
participant App as Preact App
participant Form as Form API (useForm)
participant Store as Form Store
participant Field as Field Component
User->>App: interact with UI (input / submit)
App->>Form: create instance via useForm(opts)
Form->>Store: create & manage state store
App->>Field: render form.Field (passes form)
Field->>Store: subscribe to field slice
User->>Field: type/change input
Field->>Form: field.handleChange(value)
Form->>Store: validate/update state
Store-->>Field: emit updated field state
App->>Form: form.handleSubmit()
Form->>Form: run validators and call onSubmit(values)
Estimated code review effort🎯 4 (Complex) | ⏱️ ~75 minutes Poem
🚥 Pre-merge checks | ✅ 1 | ❌ 2❌ Failed checks (2 warnings)
✅ Passed checks (1 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
89b831c to
b8189a9
Compare
There was a problem hiding this comment.
Actionable comments posted: 8
Note
Due to the large number of review comments, Critical, Major severity comments were prioritized as inline comments.
♻️ Duplicate comments (3)
docs/framework/preact/reference/type-aliases/UseField.md (2)
12-12:⚠️ Potential issue | 🔴 CriticalIncorrect source file reference for Preact documentation.
The "Defined in:" link references
packages/react-form/src/useField.tsxbut this is Preact framework documentation. This should point topackages/preact-form/src/useField.tsxinstead.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@docs/framework/preact/reference/type-aliases/UseField.md` at line 12, The "Defined in:" source link in the Preact UseField.md is pointing to the React implementation; update the reference to point to the Preact implementation by replacing the target URL `packages/react-form/src/useField.tsx` with `packages/preact-form/src/useField.tsx` in the UseField.md file so the "Defined in:" link correctly references the Preact `useField.tsx` source.
6-6:⚠️ Potential issue | 🟡 MinorVerify typo in type parameter name: "TPatentSubmitMeta".
The type parameter is spelled "TPatentSubmitMeta" which appears to be a typo for "TParentSubmitMeta". This appears consistently throughout the documentation.
Also applies to: 64-64
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@docs/framework/preact/reference/type-aliases/UseField.md` at line 6, The type parameter name "TPatentSubmitMeta" in the UseField type alias is a typo; update the type parameter declaration and every occurrence of "TPatentSubmitMeta" to "TParentSubmitMeta" (e.g., in the UseField() generic parameter list and all references within this doc entry) so the name consistently reads TParentSubmitMeta across the documentation.docs/framework/preact/reference/interfaces/WithFormProps.md (1)
8-8:⚠️ Potential issue | 🔴 CriticalIncorrect source file references for Preact documentation.
The "Defined in:" links reference
packages/react-form/src/createFormHook.tsxbut this is Preact framework documentation. These should point topackages/preact-form/src/createFormHook.tsxinstead.Also applies to: 84-84, 94-94
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@docs/framework/preact/reference/interfaces/WithFormProps.md` at line 8, Update the incorrect "Defined in:" repository links in the WithFormProps.md Preact doc: replace any references that point to packages/react-form/src/createFormHook.tsx with the corresponding packages/preact-form/src/createFormHook.tsx path so the Preact docs link to the Preact source; search the same WithFormProps.md for other "Defined in:" occurrences (including the other instances reported) and update them the same way to ensure all source links reference the preact-form implementation.
🟡 Minor comments (35)
packages/preact-form/README.md-21-21 (1)
21-21:⚠️ Potential issue | 🟡 MinorMalformed URL in Best of JS badge.
The URL contains
TanStack%formwhich appears to be a typo. It should likely beTanStack%2Fform(URL-encoded slash) to correctly referenceTanStack/form.Proposed fix
-</a><a href="https://bestofjs.org/projects/tanstack-form"><img alt="Best of JS" src="https://img.shields.io/endpoint?url=https://bestofjs-serverless.now.sh/api/project-badge?fullName=TanStack%form%26since=daily" /></a><a href="https://github.com/TanStack/form/" target="\_parent"> +</a><a href="https://bestofjs.org/projects/tanstack-form"><img alt="Best of JS" src="https://img.shields.io/endpoint?url=https://bestofjs-serverless.now.sh/api/project-badge?fullName=TanStack%2Fform%26since=daily" /></a><a href="https://github.com/TanStack/form/" target="\_parent">🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/preact-form/README.md` at line 21, The Best of JS badge URL contains a malformed encoded segment "TanStack%form" in the href; locate the badge anchor/URL (the string containing "TanStack%form" in the README) and replace that segment with the properly URL-encoded slash "TanStack%2Fform" so the badge points to "TanStack/form" (ensure the full href uses "TanStack%2Fform" rather than "TanStack%form").docs/framework/preact/reference/interfaces/UseFieldOptions.md-8-8 (1)
8-8:⚠️ Potential issue | 🟡 MinorSource file links incorrectly point to
react-forminstead ofpreact-formpackage.All preact documentation files reference
packages/react-form/src/but should referencepackages/preact-form/src/instead. The preact-form package has its own source files, and the documentation links should point there.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@docs/framework/preact/reference/interfaces/UseFieldOptions.md` at line 8, The documentation links in the Preact reference (e.g., UseFieldOptions.md) incorrectly point to "packages/react-form/src/" instead of the Preact package; update any occurrences of the string "packages/react-form/src/" in the Preact docs to "packages/preact-form/src/" so the "Defined in:" source file links resolve to the correct package (check files like UseFieldOptions.md and other docs under docs/framework/preact/reference for similar links and replace them).docs/framework/preact/reference/type-aliases/ServerFormState.md-12-12 (1)
12-12:⚠️ Potential issue | 🟡 MinorUpdate the source file link to reference
preact-forminstead ofreact-form.The
ServerFormStatetype is defined inpackages/preact-form/src/types.ts. The documentation link should point to the preact-form package source for consistency with the Preact framework documentation.Change the "Defined in:" link from
packages/react-form/src/types.ts:120topackages/preact-form/src/types.ts(verify the correct line number in preact-form).🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@docs/framework/preact/reference/type-aliases/ServerFormState.md` at line 12, Update the "Defined in:" source link for the ServerFormState doc entry to point to the Preact package source instead of React: locate the ServerFormState reference in docs/framework/preact/reference/type-aliases/ServerFormState.md and replace the existing link to packages/react-form/src/types.ts:120 with the corresponding packages/preact-form/src/types.ts (adjusting the line number to the correct line where ServerFormState is defined in preact-form's types.ts) so the documentation correctly references the preact-form source.docs/framework/preact/guides/submission-handling.md-67-76 (1)
67-76:⚠️ Potential issue | 🟡 MinorNormalize product branding to
TanStack.Use
TanStack Formconsistently (Lines 67 and 76 currently useTanstack).🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@docs/framework/preact/guides/submission-handling.md` around lines 67 - 76, Replace inconsistent product branding "Tanstack Form" with the correct "TanStack Form" in the documentation text where it appears (the sentences explaining Standard Schema support and the note about input type). Update the two occurrences of the phrase "Tanstack Form" so both read "TanStack Form" to ensure consistent branding across the guide (look for the phrases around the Standard Schema/validation paragraph and the comment line about input type).docs/framework/preact/reference/functions/useStore.md-47-47 (1)
47-47:⚠️ Potential issue | 🟡 MinorAvoid duplicate heading text for overload sections.
Line 47 repeats
## Call Signature, which triggers MD024. Rename overload sections (e.g.,## Call Signature (Store)/## Call Signature (Derived)).🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@docs/framework/preact/reference/functions/useStore.md` at line 47, The duplicate "## Call Signature" heading in the useStore documentation causes MD024; update the overload section headings to unique names (for example "## Call Signature (Store)" and "## Call Signature (Derived)") so each overload under useStore has a distinct heading; locate the overload sections in docs/framework/preact/reference/functions/useStore.md and rename the second and any subsequent "## Call Signature" headings accordingly to avoid the lint error.docs/framework/preact/reference/variables/Field.md-14-16 (1)
14-16:⚠️ Potential issue | 🟡 MinorUse Preact terminology on this Preact API page.
Line 14 says “React component”, which is inconsistent for the Preact reference section. Please update wording to “Preact component” (or framework-neutral “component”).
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@docs/framework/preact/reference/variables/Field.md` around lines 14 - 16, The documentation text for the Field component incorrectly uses "React component"; update the wording in the Field.md description (the sentence describing what Field returns) to use Preact terminology — e.g., change "React component" to "Preact component" (or use the framework-neutral term "component") so the description for Field and the mention of useField remains consistent with the Preact reference.docs/framework/preact/reference/variables/Field.md-108-116 (1)
108-116:⚠️ Potential issue | 🟡 MinorFix generic type parameter typo (
TPatentSubmitMeta).
TPatentSubmitMetaappears to be a typo and makes the signature harder to trust/read. Please rename consistently (likelyTParentSubmitMetaorTSubmitMetabased on source type naming).🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@docs/framework/preact/reference/variables/Field.md` around lines 108 - 116, The docs show a typo in the generic parameter name "TPatentSubmitMeta" used inside the FieldComponentProps generic list; rename that identifier consistently to "TParentSubmitMeta" (or "TSubmitMeta" if that matches your source types) across this documentation entry and any corresponding type references so the signature reads FieldComponentProps<..., TParentSubmitMeta> and any cross-references are updated; verify the underlying source/type names (e.g., the type exported in code) and make the doc generic name match that exact exported type name.docs/framework/preact/reference/interfaces/UseFieldOptionsBound.md-8-8 (1)
8-8:⚠️ Potential issue | 🟡 MinorCorrect the “Defined in” path for Preact docs
Line 8 references
packages/react-form/src/types.ts, which is inconsistent for this Preact reference page.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@docs/framework/preact/reference/interfaces/UseFieldOptionsBound.md` at line 8, The "Defined in" link in the Preact doc UseFieldOptionsBound references the React package path; update that reference to point to the Preact source file instead (replace packages/react-form/src/types.ts:81 with the correct Preact source path, e.g., packages/preact-form/src/types.ts:81) in the UseFieldOptionsBound doc so the "Defined in" link resolves to the Preact implementation.docs/framework/preact/reference/interfaces/ReactFormApi.md-8-8 (1)
8-8:⚠️ Potential issue | 🟡 MinorUse a Preact-relevant source link in this page
Line 8 currently points to
packages/react-form/...; this is likely the wrong source attribution for a Preact reference page.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@docs/framework/preact/reference/interfaces/ReactFormApi.md` at line 8, The source link in the Preact reference page ReactFormApi.md points to the React implementation (packages/react-form/src/useForm.tsx); update that link to the Preact-specific source file for the same symbol (replace the React package path with the Preact package counterpart, e.g., change packages/react-form/src/useForm.tsx to the corresponding packages/preact-form/src/useForm.tsx or the actual Preact file that defines useForm) so the "Defined in:" link references the Preact implementation of useForm.docs/framework/preact/reference/variables/useIsomorphicLayoutEffect.md-37-39 (1)
37-39:⚠️ Potential issue | 🟡 MinorReplace React-version-specific metadata on a Preact page
Line 39 shows
16.8.0, which reads like React versioning and can mislead in this framework section. Prefer package-version context or remove this section.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@docs/framework/preact/reference/variables/useIsomorphicLayoutEffect.md` around lines 37 - 39, The "## Version" metadata block currently lists "16.8.0" (a React-style version) which is misleading in this Preact doc; either remove the "## Version" section entirely or replace "16.8.0" with the appropriate Preact/package version string or a generic package-version label. Locate the "## Version" header and the literal "16.8.0" in useIsomorphicLayoutEffect.md and update the text to reflect package-version context (e.g., "Package version: x.y.z" or the correct Preact release) or delete the header and value if versioning isn't applicable.docs/framework/preact/reference/interfaces/ReactFormApi.md-72-72 (1)
72-72:⚠️ Potential issue | 🟡 MinorAlign framework wording in the field description
Line 72 says “A React component…”. In this section it should be phrased as a Preact component (or framework-neutral wording).
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@docs/framework/preact/reference/interfaces/ReactFormApi.md` at line 72, The wording in the field description for ReactFormApi incorrectly says "A React component…"; update the description in the ReactFormApi documentation to use framework-neutral wording or explicitly say "A Preact component" (e.g., change "A React component to render form fields." to "A Preact component to render form fields." or "A component to render form fields.") so the description matches the framework; edit the ReactFormApi interface doc entry (the paragraph under the ReactFormApi header) to implement this wording change.docs/framework/preact/reference/functions/createFormHookContexts.md-12-12 (1)
12-12:⚠️ Potential issue | 🟡 MinorUpdate source attribution to match Preact package docs
Line 12 currently links to
packages/react-form/...; this should be aligned with the Preact package/source context.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@docs/framework/preact/reference/functions/createFormHookContexts.md` at line 12, The documentation source attribution currently points to the React package; update the link in createFormHookContexts.md (the "Defined in" reference) to the corresponding Preact source (e.g., change packages/react-form/src/createFormHook.tsx:95 to packages/preact-form/src/createFormHook.tsx:95) so the "Defined in" attribution correctly references the Preact implementation of createFormHook (createFormHook.tsx).docs/framework/preact/reference/functions/useForm.md-12-15 (1)
12-15:⚠️ Potential issue | 🟡 MinorMake this reference consistently Preact-oriented
Line 12 points to
packages/react-form/...and Line 14 says “React Hook”. In the Preact section, both should be Preact-specific (or explicitly framework-neutral).🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@docs/framework/preact/reference/functions/useForm.md` around lines 12 - 15, The docs reference for useForm is React-specific; update the reference to be Preact-oriented by changing the source link from packages/react-form/... to the Preact implementation (e.g., packages/preact-form/src/useForm.tsx) and update the description text from “A custom React Hook” to either “A custom Preact Hook” or a framework-neutral phrase like “A custom Hook that returns an extended instance of the FormApi class”; ensure the symbol name useForm and FormApi remain unchanged in the text.docs/framework/preact/guides/debugging.md-39-39 (1)
39-39:⚠️ Potential issue | 🟡 MinorMinor grammar fix in TypeScript explanation
Line 39: “TypeScript struggled” should be present tense (“struggles”) in this guidance context.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@docs/framework/preact/guides/debugging.md` at line 39, Replace the past-tense phrase "TypeScript struggled" with present tense "TypeScript struggles" in the sentence that currently reads "There are some edge cases where TypeScript struggled with the complexity of our types." to keep the guidance in present tense; locate the sentence by searching for the exact phrase "TypeScript struggled" in the docs/framework/preact/guides/debugging.md content and update it to "TypeScript struggles".docs/config.json-505-533 (1)
505-533:⚠️ Potential issue | 🟡 MinorPreact API nav is missing links to newly added reference pages
This block only exposes a subset of the new Preact reference docs. Pages added in this PR (e.g.,
createFormHookContexts,ReactFormApi,UseFieldOptionsBound,useIsomorphicLayoutEffect) should also be included to avoid orphaned docs.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@docs/config.json` around lines 505 - 533, The Preact nav block currently only includes a subset of reference pages; add entries for the newly added reference pages so they aren't orphaned — specifically add child items with labels and "to" targets for createFormHookContexts (e.g., "framework/preact/reference/functions/createFormHookContexts" or appropriate path), ReactFormApi (e.g., "framework/preact/reference/interfaces/ReactFormApi" or appropriate path), UseFieldOptionsBound (e.g., "framework/preact/reference/type-aliases/UseFieldOptionsBound"), and useIsomorphicLayoutEffect (e.g., "framework/preact/reference/functions/useIsomorphicLayoutEffect") alongside the existing items under the "preact" -> "children" array in the same nav block (matching naming convention used for other entries).docs/framework/preact/reference/variables/useIsomorphicLayoutEffect.md-12-12 (1)
12-12:⚠️ Potential issue | 🟡 MinorFix framework source attribution in this Preact reference
Line 12 links to
packages/react-form/..., which is confusing in a Preact API page. Prefer thepackages/preact-form/...source (or a framework-neutral source note).Suggested docs fix
-Defined in: [packages/react-form/src/useIsomorphicLayoutEffect.ts:3](https://github.com/TanStack/form/blob/main/packages/react-form/src/useIsomorphicLayoutEffect.ts#L3) +Defined in: [packages/preact-form/src/useIsomorphicLayoutEffect.ts:3](https://github.com/TanStack/form/blob/main/packages/preact-form/src/useIsomorphicLayoutEffect.ts#L3)🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@docs/framework/preact/reference/variables/useIsomorphicLayoutEffect.md` at line 12, The reference currently points to the React package source (packages/react-form/src/useIsomorphicLayoutEffect.ts) which is misleading on the Preact API page; update the "Defined in" link in docs/framework/preact/reference/variables/useIsomorphicLayoutEffect.md to point to the Preact implementation (packages/preact-form/src/useIsomorphicLayoutEffect.ts) or replace the link with a framework-neutral note that cites the shared implementation location, ensuring the displayed file path and URL reflect the preact-form package or neutral wording so readers aren’t directed to the react-form package.docs/framework/preact/guides/listeners.md-71-71 (1)
71-71:⚠️ Potential issue | 🟡 MinorFix heading level for "Built-in Debouncing" section.
The "Built-in Debouncing" section uses
###(h3) but should use##(h2) since it's a top-level section, not a subsection of the preceding example. As per static analysis, heading levels should only increment by one level at a time.📝 Proposed fix
-### Built-in Debouncing +## Built-in Debouncing🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@docs/framework/preact/guides/listeners.md` at line 71, Change the heading level for the "Built-in Debouncing" section from an h3 to an h2 so heading levels only increment by one; locate the heading text "Built-in Debouncing" in docs and replace the leading "###" with "##" to make it a top-level section rather than a subsection.docs/framework/preact/reference/type-aliases/FieldComponent.md-6-6 (1)
6-6:⚠️ Potential issue | 🟡 MinorFix typo in type parameter name: "TPatentSubmitMeta" → "TParentSubmitMeta"
The type parameter is misspelled as "TPatentSubmitMeta" and should be "TParentSubmitMeta" to match the correct spelling used throughout the codebase (e.g., in
packages/form-core/src/FieldApi.ts). This typo appears at line 6 (in the title) and line 65 (in the type parameters section).🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@docs/framework/preact/reference/type-aliases/FieldComponent.md` at line 6, The type parameter name is misspelled in the FieldComponent declaration and its type-parameters list: replace the incorrect "TPatentSubmitMeta" with the correct "TParentSubmitMeta" wherever it appears in the FieldComponent definition and related type-parameter section so it matches the spelling used in FieldApi and the rest of the codebase; update both the title and the detailed type parameters block for the FieldComponent type alias to use TParentSubmitMeta.docs/framework/preact/guides/focus-management.md-133-133 (1)
133-133:⚠️ Potential issue | 🟡 MinorType mismatch:
valueprop expects string butfield.state.valueis a number.The
TextInputcomponent expects a string forvalue, butdefaultValues.ageis0(number). This should be converted to a string.Proposed fix
- value={field.state.value} + value={String(field.state.value)}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@docs/framework/preact/guides/focus-management.md` at line 133, The TextInput's value prop is receiving a number (field.state.value / defaultValues.age) but expects a string; update where TextInput is rendered (reference TextInput and field.state.value/defaultValues.age) to coerce the numeric value to a string (e.g., use String(field.state.value) or field.state.value?.toString() or a fallback like '' for null/undefined) so the prop type matches and the input remains controlled.docs/framework/preact/reference/functions/useField.md-12-12 (1)
12-12:⚠️ Potential issue | 🟡 MinorIncorrect source link points to React package instead of Preact.
The "Defined in" link references
packages/react-form/src/useField.tsxbut this documentation is for the Preact integration. It should point to the Preact package source file.Proposed fix
-Defined in: [packages/react-form/src/useField.tsx:139](https://github.com/TanStack/form/blob/main/packages/react-form/src/useField.tsx#L139) +Defined in: [packages/preact-form/src/useField.tsx:139](https://github.com/TanStack/form/blob/main/packages/preact-form/src/useField.tsx#L139)🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@docs/framework/preact/reference/functions/useField.md` at line 12, Update the "Defined in" source link in docs/framework/preact/reference/functions/useField.md so it points to the Preact package source instead of the React one: replace the reference to packages/react-form/src/useField.tsx with the Preact file path (packages/preact-form/src/useField.tsx) so the "Defined in" link correctly targets the Preact implementation of useField.docs/framework/preact/quick-start.md-109-110 (1)
109-110:⚠️ Potential issue | 🟡 MinorValidation logic inconsistent with error message.
The validator returns an error when
valueis not greater than 13, but the error message says "Must be 13 or older". This means a 13-year-old would incorrectly receive an error. The condition should use>=to match the message.Proposed fix
onChange: ({ value }) => - value > 13 ? undefined : 'Must be 13 or older', + value >= 13 ? undefined : 'Must be 13 or older',🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@docs/framework/preact/quick-start.md` around lines 109 - 110, The validation logic in the onChange validator currently uses value > 13 but the message says "Must be 13 or older", so update the validator used in the onChange handler to use value >= 13 (so 13 is accepted) and keep the error message 'Must be 13 or older' unchanged; locate the onChange function in the Quick Start Preact example and replace the greater-than check with a greater-than-or-equal check.docs/framework/preact/guides/reactivity.md-34-47 (1)
34-47:⚠️ Potential issue | 🟡 MinorCode example has multiple issues that would prevent it from working.
form.Fieldis missing the requirednamepropfield.state.lastNameshould befield.state.value- the field's value is accessed viastate.value, not by the field nameonInput={field.handleChange}won't work -handleChangeexpects the new value directly, not an event objectProposed fix
<form.Subscribe selector={(state) => state.values.firstName} children={(firstName) => ( - <form.Field> + <form.Field name="lastName"> {(field) => ( <input name="lastName" - value={field.state.lastName} - onInput={field.handleChange} + value={field.state.value} + onInput={(e) => field.handleChange(e.target.value)} /> )} </form.Field> )} />🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@docs/framework/preact/guides/reactivity.md` around lines 34 - 47, The example has three bugs: update the form.Field invocation to include the required name prop (e.g., name="lastName"); read the field value from field.state.value instead of field.state.lastName; and change the input handler so it passes the new value to field.handleChange (replace passing the event with calling handleChange with the input's current value, e.g., extract event.currentTarget.value and pass that to field.handleChange). These changes touch form.Subscribe, its selector, the form.Field component, the use of field.state, and the onInput handler for the input element.docs/framework/preact/reference/functions/useField.md-106-108 (1)
106-108:⚠️ Potential issue | 🟡 MinorFix type parameter name:
TPatentSubmitMetashould beTParentSubmitMeta.This typo appears in the source code (packages/preact-form/src/useField.tsx and packages/react-form/src/useField.tsx) and is propagated to the documentation. The correct naming
TParentSubmitMetais consistently used across vue, svelte, and core form implementations.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@docs/framework/preact/reference/functions/useField.md` around lines 106 - 108, The type parameter name is misspelled as TPatentSubmitMeta (should be TParentSubmitMeta) in the useField implementations and the docs; update every occurrence of the generic/type parameter from TPatentSubmitMeta to TParentSubmitMeta in the useField function/type declarations and usages (e.g., the useField generic signature and any references within packages/preact-form and packages/react-form) and correct the documentation entry (useField.md) to match the canonical TParentSubmitMeta name so all implementations are consistent.docs/framework/preact/reference/type-aliases/LensFieldComponent.md-15-15 (1)
15-15:⚠️ Potential issue | 🟡 MinorSource path incorrectly references react-form instead of preact-form.
The source definition points to
packages/react-form/src/useField.tsxbut this is Preact documentation. Should referencepackages/preact-form/src/useField.tsx.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@docs/framework/preact/reference/type-aliases/LensFieldComponent.md` at line 15, The documentation entry in LensFieldComponent.md incorrectly links the source to packages/react-form/src/useField.tsx:598; update the source reference to packages/preact-form/src/useField.tsx:598 inside docs/framework/preact/reference/type-aliases/LensFieldComponent.md so the Preact docs point to the correct source file (replace the react-form path with preact-form in the Defined in link).docs/framework/preact/reference/type-aliases/LensFieldComponent.md-19-34 (1)
19-34:⚠️ Potential issue | 🟡 MinorDuplicate "## Type Parameters" heading.
There are two
## Type Parametersheadings (lines 19 and 34), which creates confusing document structure. The first section (lines 19-32) documentsTLensData,TParentSubmitMeta, andExtendedApi, while the second (line 34+) documentsTName,TData, and validator generics. Consider renaming the second heading to distinguish it, e.g., "## Call Signature Type Parameters".🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@docs/framework/preact/reference/type-aliases/LensFieldComponent.md` around lines 19 - 34, The document contains two identical "## Type Parameters" headings; rename the second heading (the one documenting TName, TData and validator generics) to a distinct title such as "## Call Signature Type Parameters" (or another descriptive phrase) to avoid duplication and clarify structure; update the heading that surrounds the TName, TData and validator generic entries and ensure any cross-references point to the new heading while leaving TLensData, TParentSubmitMeta and ExtendedApi unchanged.packages/preact-form/tests/useForm.test.tsx-918-919 (1)
918-919:⚠️ Potential issue | 🟡 MinorESLint rule
@eslint-react/no-array-index-keyis not defined.The disable comment references a rule that isn't configured in the project's ESLint setup. Either add the
@eslint-reactplugin to the ESLint configuration, or if this was copied from the React tests, update to use the correct rule name for this project's setup (possiblyreact/no-array-index-keyor remove the comment if no such rule exists).🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/preact-form/tests/useForm.test.tsx` around lines 918 - 919, The ESLint disable comment referencing `@eslint-react/no-array-index-key` is invalid; update the disable or remove it: either change the comment to the correct rule name used in this project (e.g., react/no-array-index-key) or simply delete the disable line above the JSX element <form.Field key={i} name={`foo[${i}].name`}>; ensure the fix targets the test file's JSX usage of key={i} on form.Field so the linter no longer references the non-existent `@eslint-react` plugin.docs/framework/preact/guides/basic-concepts.md-86-95 (1)
86-95:⚠️ Potential issue | 🟡 MinorInvalid ESLint rule configuration syntax.
The ESLint rule example uses
trueas the severity, which is not a valid ESLint severity level. ESLint expects"error","warn","off", or numeric values2,1,0.📝 Proposed fix
```json "rules": { "react/no-children-prop": [ - true, + "error", { "allowFunctions": true } ], }</details> <details> <summary>🤖 Prompt for AI Agents</summary>Verify each finding against the current code and only fix it if needed.
In
@docs/framework/preact/guides/basic-concepts.mdaround lines 86 - 95, The
ESLint rule example uses an invalid severity value; update the
"react/no-children-prop" rule configuration in the shown JSON to use a valid
severity ("error", "warn", "off" or numeric 2/1/0) instead of true so ESLint
accepts the config; locate the rule entry for "react/no-children-prop" and
replace the first array element (currently true) with a valid severity like
"error" while keeping the options object (allowFunctions) unchanged.</details> </blockquote></details> <details> <summary>packages/preact-form/src/useFieldGroup.tsx-76-79 (1)</summary><blockquote> `76-79`: _⚠️ Potential issue_ | _🟡 Minor_ **JSDoc comment incorrectly references "React" instead of "Preact".** The comment says "A React component" but this is the Preact integration package. <details> <summary>📝 Proposed fix</summary> ```diff /** - * A React component to render form fields. With this, you can render and manage individual form fields. + * A Preact component to render form fields. With this, you can render and manage individual form fields. */ Field: LensFieldComponent<TFieldGroupData, TSubmitMeta>🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/preact-form/src/useFieldGroup.tsx` around lines 76 - 79, The JSDoc for the Field export incorrectly says "A React component"; update the comment to reference Preact instead—e.g., change "A React component to render form fields" to "A Preact component to render form fields" (or "A Preact-compatible component") to accurately describe Field: LensFieldComponent<TFieldGroupData, TSubmitMeta> in useFieldGroup.tsx.docs/framework/preact/guides/form-composition.md-466-469 (1)
466-469:⚠️ Potential issue | 🟡 MinorThe prop name here should be
fields.Line 469 tells readers to change the
fieldproperty, but the API and the examples below usefields. As written, this points people at a non-existent prop.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@docs/framework/preact/guides/form-composition.md` around lines 466 - 469, The documentation incorrectly refers to a non-existent prop named "field"; update the wording so it instructs readers to change the "fields" property instead (update the sentence in the "Mapping field group values to a different field" section and any nearby example code or prose that mentions `field` to use `fields`), ensuring consistency with the API and the examples that follow.docs/framework/preact/reference/functions/createFormHook.md-12-12 (1)
12-12:⚠️ Potential issue | 🟡 MinorPoint the "Defined in" link at the Preact source.
Line 12 still links to
packages/react-form/src/createFormHook.tsx, so this Preact reference page sends readers to the wrong package. If this page is generated, please fix the source mapping/template rather than patching the markdown by hand.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@docs/framework/preact/reference/functions/createFormHook.md` at line 12, The "Defined in" link on the Preact page points to the React package; update the docs generator/template that produces createFormHook.md so it resolves the source file for the Preact variant instead of hardcoding the React path. Locate the template or mapping code that emits the "Defined in" link (the logic that builds the repository path for createFormHook) and change it to use the Preact package source (e.g., packages/preact-form/src/createFormHook.tsx) when rendering the Preact reference page (createFormHook.md) rather than packages/react-form/src/createFormHook.tsx; ensure the generator uses the package type or component namespace to pick the correct package path.docs/framework/preact/guides/form-composition.md-258-263 (1)
258-263:⚠️ Potential issue | 🟡 MinorThese snippets are spreading the
formOptionsfactory, not the options object.Lines 260, 290, and 300 use
...formOptions, butformOptionsis the helper function. These examples need the object returned by it (formOptsor similar), otherwise they fail as soon as someone copies them.Also applies to: 285-300
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@docs/framework/preact/guides/form-composition.md` around lines 258 - 263, The examples are spreading the helper function formOptions instead of the returned options object; update the snippets (e.g., in RouteComponent where useAppForm is called) to call the factory and spread its result (assign const formOpts = formOptions(...) or call formOptions() and spread formOpts) so useAppForm receives the actual options object rather than the function itself; apply the same change to the other affected examples around the 285–300 range.docs/framework/preact/guides/form-composition.md-573-577 (1)
573-577:⚠️ Potential issue | 🟡 MinorMake the lazy import match the file name used above.
The previous snippet defines
src/components/text-field.tsx, but Line 577 imports../components/text-fields.tsx. One of those names needs to change or this example won't resolve.Suggested fix
-const TextField = lazy(() => import('../components/text-fields.tsx')) +const TextField = lazy(() => import('../components/text-field.tsx'))🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@docs/framework/preact/guides/form-composition.md` around lines 573 - 577, The lazy import path for TextField is inconsistent with the component name used earlier: change the import in the createFormHook file so the lazy call matches the actual file name (e.g., import '../components/text-field.tsx' to align with src/components/text-field.tsx) by updating the lazy(() => import('../components/text-fields.tsx')) line to the correct filename that matches the component (ensuring the TextField constant continues to reference the lazy import).packages/preact-form/tests/createFormHook.test-d.tsx-435-439 (1)
435-439:⚠️ Potential issue | 🟡 MinorThis
@ts-expect-erroris failing for the wrong reason.Line 438 passes
name=""instead offields="person", so this stays green because of an unknown/missing prop rather than becauseincompatibleAppFormviolates the subset type you're trying to cover.Suggested fix
- name="" + fields="person"🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/preact-form/tests/createFormHook.test-d.tsx` around lines 435 - 439, The `@ts-expect-error` on the FormGroup test is masking the wrong problem because the component is given name="" instead of the intended fields prop; update the test to pass fields="person" (or the correct fields value used elsewhere) to the FormGroup invocation so the type-checker fails due to incompatibleAppForm not matching the expected subset type (keep form={incompatibleAppForm} and prop1="test" as-is), ensuring the error is caused by the form type mismatch rather than a missing/unknown prop.docs/framework/preact/guides/form-composition.md-154-157 (1)
154-157:⚠️ Potential issue | 🟡 MinorUse a JSX comment inside
<form.AppForm>.The
// ...lines here are parsed as JSX children, so this snippet won't compile as copied.Suggested fix
<form.AppForm> - // Notice the `AppForm` component wrapper; `AppForm` provides the required - context + {/* Notice the `AppForm` component wrapper; `AppForm` provides the required context */} <form.SubscribeButton label="Submit" /> </form.AppForm>🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@docs/framework/preact/guides/form-composition.md` around lines 154 - 157, The JSX snippet uses JavaScript line comments inside a JSX element which become children and break compilation; update the <form.AppForm> example so the explanatory note is a proper JSX comment (e.g., replace the "// Notice..." line with a {/* ... */} comment) or remove the inline comment entirely, keeping the component wrapper and <form.SubscribeButton label="Submit" /> intact (look for AppForm, form.AppForm, and SubscribeButton to locate the snippet).packages/preact-form/src/useField.tsx-222-231 (1)
222-231:⚠️ Potential issue | 🟡 MinorArray-mode selector can miss sparse-array length semantics.
Using
Object.keys(value).lengthcan diverge from actual arraylengthfor sparse arrays. This can skip expected reactivity updates for array fields that contain holes.💡 Suggested fix
- const reactiveStateValue = useStore( - fieldApi.store, - (opts.mode === 'array' - ? (state) => Object.keys((state.value as unknown) ?? []).length - : (state) => state.value) as ( - state: typeof fieldApi.state, - ) => TData | number, - ) + const reactiveStateValue = useStore( + fieldApi.store, + (opts.mode === 'array' + ? (state) => + Array.isArray(state.value) ? state.value.length : 0 + : (state) => state.value) as ( + state: typeof fieldApi.state, + ) => TData | number, + )🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/preact-form/src/useField.tsx` around lines 222 - 231, The selector used for array mode in useStore (reactiveStateValue) uses Object.keys((state.value) ?? []).length which misrepresents sparse arrays; update the selector so when opts.mode === 'array' it checks if state.value is an actual array and returns (state.value as any).length (with safe null/undefined handling), falling back to Object.keys(...) length only for non-array values—i.e., change the array-mode branch in the useStore selector to prefer Array.isArray(state.value) ? state.value.length : Object.keys((state.value ?? [])).length to preserve sparse-array length semantics for fieldApi.store reactivity.
🧹 Nitpick comments (6)
packages/preact-form/eslint.config.js (1)
12-16: Rule override lacksfilesscope and may apply globally.The rule to disable
@eslint-react/no-use-contextis in a separate config object without afilespattern. While this likely works since the rule only applies to files the React plugin processes, it would be cleaner to merge it into the same config block or add matchingfilesfor consistency.Suggested consolidation
export default [ ...rootConfig, { files: ['**/*.{ts,tsx}'], ...pluginReact.configs.recommended, + rules: { + ...pluginReact.configs.recommended.rules, + '@eslint-react/no-use-context': 'off', + }, }, - { - rules: { - '@eslint-react/no-use-context': 'off', - }, - }, ]🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/preact-form/eslint.config.js` around lines 12 - 16, The ESLint rule override for '@eslint-react/no-use-context' is declared in a separate config object without a files scope, which can make it apply more broadly and is inconsistent; merge this rule into the primary rules block (the existing rules object in eslint.config.js) or add a matching files array for the separate config so the override is scoped; update the rules entry for '@eslint-react/no-use-context' within the main rules object (or add files: [...] alongside the rule) to ensure consistent, scoped configuration.docs/framework/preact/guides/debugging.md (1)
13-13: Use a current/framework-appropriate docs link in the warning textLine 13 embeds a legacy
reactjs.orgURL. Consider updating to a current link (or a framework-neutral explanation) for better guidance quality.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@docs/framework/preact/guides/debugging.md` at line 13, The embedded warning string "Warning: A component is changing an uncontrolled input to be controlled. ... More info: https://reactjs.org/link/controlled-components" should be updated to use a current, framework-appropriate link or a neutral explanation; locate that exact warning text in docs/framework/preact/guides/debugging.md and replace the reactjs.org URL with either the official Preact guidance URL (or a stable framework-neutral docs page) and/or append a brief in-place explanation about controlled vs uncontrolled inputs so readers aren’t directed to a legacy React-specific link.docs/framework/preact/guides/ui-libraries.md (1)
54-60: UsingdefaultValuecreates an uncontrolled component.Using
defaultValueinstead ofvaluemakes theTextInputuncontrolled, meaning the component won't update when form state changes programmatically (e.g., viaform.reset()orfield.setValue()). For fully controlled behavior, usevalueinstead.Proposed fix
<TextInput - defaultValue={state.value} + value={state.value} onInput={(e) => handleChange(e.target.value)} onBlur={handleBlur} placeholder="Enter your name" />The same applies to the Material UI example at Lines 102-103.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@docs/framework/preact/guides/ui-libraries.md` around lines 54 - 60, The examples use defaultValue which makes the inputs uncontrolled; change the TextInput props to use value={state.value} (instead of defaultValue) so the component is fully controlled and will reflect programmatic updates, keep onInput={(e) => handleChange(e.target.value)} and onBlur={handleBlur} as-is to drive state updates via handleChange/handleBlur; apply the same change to the Material UI example (replace defaultValue with value tied to the same form state/field.setValue source) so both inputs remain controlled by the form state.packages/preact-form/tests/useForm.test-d.tsx (1)
4-4: RenameReactFormExtendedApitoFormExtendedApiorPreactFormExtendedApi.The Preact package defines its own
ReactFormExtendedApitype independently from the React package. The "React" prefix in the Preact package is misleading and reduces clarity. Consider using a more generic or Preact-specific name.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/preact-form/tests/useForm.test-d.tsx` at line 4, The type name ReactFormExtendedApi in the Preact package is misleading; rename the type to either FormExtendedApi or PreactFormExtendedApi and update all usages and exports accordingly. Locate the type declaration named ReactFormExtendedApi in src/useForm and rename it, update the import in tests (useForm.test-d.tsx) to the new name, and update any re-exports (e.g., package index or barrel files) and references in functions like useForm to use the new identifier so the public API remains consistent.docs/framework/preact/guides/validation.md (1)
34-35: Consider usinge.currentTargetfor consistency with Preact best practices.The documentation uses
e.target.valueAsNumberande.target.valuein examples. While this works,e.currentTargetprovides better TypeScript type inference in Preact/React event handlers since it's guaranteed to be the element the handler is attached to. Consider updating for consistency with the example inexamples/preact/simple/src/index.tsxwhich usese.currentTarget.value.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@docs/framework/preact/guides/validation.md` around lines 34 - 35, Update the event handler to use e.currentTarget for better TypeScript inference: replace uses of e.target.valueAsNumber with e.currentTarget.valueAsNumber in the onInput handler that calls field.handleChange; ensure other examples (where e.target.value is used) are similarly updated to e.currentTarget.value to match the pattern used in examples/preact/simple/src/index.tsx.packages/preact-form/src/useField.tsx (1)
351-352: Docs still reference React in the Preact adapter.These comments mention “React element/component” in a Preact package. Consider renaming to framework-neutral or Preact wording for consistency.
Also applies to: 682-683
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/preact-form/src/useField.tsx` around lines 351 - 352, The JSDoc for the children param in useField.tsx and the similar comment around the other occurrence still mention “React element/component”; update those comments to be framework-neutral or Preact-specific (e.g., “Preact element/component” or “JSX element/component”) so the Preact adapter docs are consistent—search for the JSDoc on the children parameter (and the other comment referenced) in useField.tsx and replace “React” wording accordingly.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 747bd639-a127-45a5-8cf4-1e11b0083504
⛔ Files ignored due to path filters (1)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (65)
docs/config.jsondocs/framework/preact/guides/arrays.mddocs/framework/preact/guides/async-initial-values.mddocs/framework/preact/guides/basic-concepts.mddocs/framework/preact/guides/custom-errors.mddocs/framework/preact/guides/debugging.mddocs/framework/preact/guides/dynamic-validation.mddocs/framework/preact/guides/focus-management.mddocs/framework/preact/guides/form-composition.mddocs/framework/preact/guides/linked-fields.mddocs/framework/preact/guides/listeners.mddocs/framework/preact/guides/reactivity.mddocs/framework/preact/guides/submission-handling.mddocs/framework/preact/guides/ui-libraries.mddocs/framework/preact/guides/validation.mddocs/framework/preact/quick-start.mddocs/framework/preact/reference/functions/createFormHook.mddocs/framework/preact/reference/functions/createFormHookContexts.mddocs/framework/preact/reference/functions/useField.mddocs/framework/preact/reference/functions/useFieldGroup.mddocs/framework/preact/reference/functions/useForm.mddocs/framework/preact/reference/functions/useStore.mddocs/framework/preact/reference/index.mddocs/framework/preact/reference/interfaces/ReactFormApi.mddocs/framework/preact/reference/interfaces/UseFieldOptions.mddocs/framework/preact/reference/interfaces/UseFieldOptionsBound.mddocs/framework/preact/reference/interfaces/WithFieldGroupProps.mddocs/framework/preact/reference/interfaces/WithFormProps.mddocs/framework/preact/reference/type-aliases/FieldComponent.mddocs/framework/preact/reference/type-aliases/LensFieldComponent.mddocs/framework/preact/reference/type-aliases/ReactFormExtendedApi.mddocs/framework/preact/reference/type-aliases/ServerFormState.mddocs/framework/preact/reference/type-aliases/UseField.mddocs/framework/preact/reference/variables/Field.mddocs/framework/preact/reference/variables/useIsomorphicLayoutEffect.mdexamples/preact/simple/README.mdexamples/preact/simple/index.htmlexamples/preact/simple/package.jsonexamples/preact/simple/src/index.tsxexamples/preact/simple/tsconfig.jsonexamples/preact/simple/vite.config.tspackages/preact-form/README.mdpackages/preact-form/eslint.config.jspackages/preact-form/package.jsonpackages/preact-form/src/createFormHook.tsxpackages/preact-form/src/index.tspackages/preact-form/src/types.tspackages/preact-form/src/useField.tsxpackages/preact-form/src/useFieldGroup.tsxpackages/preact-form/src/useForm.tsxpackages/preact-form/src/useFormId.tspackages/preact-form/src/useIsomorphicLayoutEffect.tspackages/preact-form/tests/createFormHook.test-d.tsxpackages/preact-form/tests/createFormHook.test.tsxpackages/preact-form/tests/test-setup.tspackages/preact-form/tests/useField.test-d.tsxpackages/preact-form/tests/useField.test.tsxpackages/preact-form/tests/useForm.test-d.tsxpackages/preact-form/tests/useForm.test.tsxpackages/preact-form/tests/utils.tspackages/preact-form/tsconfig.docs.jsonpackages/preact-form/tsconfig.jsonpackages/preact-form/vite.config.tspnpm-workspace.yamlscripts/generate-docs.ts
| import { useForm } from '@tanstack/preact-form' | ||
| import { useQuery } from '@tanstack/react-query' | ||
|
|
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Find framework-mismatched TanStack Query imports under Preact docs
rg -n '@tanstack/react-query|@tanstack/solid-query|@tanstack/vue-query' docs/framework/preactRepository: TanStack/form
Length of output: 161
🏁 Script executed:
#!/bin/bash
# Check if `@tanstack/preact-query` is referenced elsewhere in Preact guides
rg -n '@tanstack/preact-query' docs/framework/preact --max-count=5
# Also check the full context of the problematic file
echo "--- Full content of async-initial-values.md ---"
cat -n docs/framework/preact/guides/async-initial-values.md | head -30Repository: TanStack/form
Length of output: 1548
🏁 Script executed:
#!/bin/bash
# Check what the correct import should be by looking at other framework guides
echo "=== React guide imports ==="
rg -n 'import.*useQuery' docs/framework/react/guides/async-initial-values.md 2>/dev/null | head -3
echo "=== Vue guide imports ==="
rg -n 'import.*useQuery' docs/framework/vue/guides/async-initial-values.md 2>/dev/null | head -3
echo "=== Solid guide imports ==="
rg -n 'import.*useQuery' docs/framework/solid/guides/async-initial-values.md 2>/dev/null | head -3
# Check if `@tanstack/preact-query` is mentioned anywhere in the repo
echo "=== References to preact-query in repo ==="
rg '@tanstack/preact-query' --type-list | head -20
rg -l '@tanstack/preact-query' 2>/dev/null | head -10Repository: TanStack/form
Length of output: 690
🏁 Script executed:
#!/bin/bash
# Check if preact-query package exists by looking at package management files
echo "=== Looking for preact-query references in package.json files ==="
find . -name "package.json" -type f | xargs grep -l "preact-query" 2>/dev/null
echo "=== Check main package.json for Query packages ==="
jq '.dependencies | keys[] | select(contains("query"))' package.json 2>/dev/null || cat package.json | grep -i "query" | head -10
echo "=== Check root package references ==="
rg -i "preact.*query|@tanstack.*query" --glob="package.json" --glob="package-lock.json" | head -15Repository: TanStack/form
Length of output: 403
🏁 Script executed:
#!/bin/bash
# Search for any Solid guide and check its query usage
echo "=== Solid async-initial-values guide ==="
cat -n docs/framework/solid/guides/async-initial-values.md 2>/dev/null | head -30 || echo "File not found"
# Check if there's documentation about Preact + Query
echo "=== Search for Preact Query documentation in repo ==="
rg -i "preact.*query|preact.*tanstack" docs/ --type md | head -10
# Check package ecosystem - does `@tanstack/preact-query` exist?
echo "=== Check if README or docs mention preact-query ==="
rg "@tanstack/preact-query" . 2>/dev/null | head -5 || echo "No `@tanstack/preact-query` found"Repository: TanStack/form
Length of output: 2127
🌐 Web query:
@tanstack/preact-query npm package
💡 Result:
@tanstack/preact-query is the Preact adapter for TanStack Query (server-state fetching/caching/synchronization). It provides Preact-friendly exports like QueryClient, QueryClientProvider, and hooks such as useQuery, useMutation, etc. [3][4]
Install
npm i `@tanstack/preact-query`
# or pnpm add / yarn add / bun add[1]
Basic usage
import { render } from 'preact'
import { QueryClient, QueryClientProvider, useQuery } from '@tanstack/preact-query'
const queryClient = new QueryClient()
function App() {
const q = useQuery({
queryKey: ['todos'],
queryFn: () => fetch('/api/todos').then(r => r.json()),
})
return <pre>{JSON.stringify(q.data, null, 2)}</pre>
}
render(
<QueryClientProvider client={queryClient}>
<App />
</QueryClientProvider>,
document.getElementById('app')!,
)[2][3]
Latest version (as of Mar 31, 2026): 5.95.2 [5]
Docs: TanStack Query → Preact section (installation/quick start/guides). [1][2]
References: [1] (tanstack.com) [2] (tanstack.com) [3] (tanstack.com) [4] (tanstack.com) [5] (newreleases.io)
Citations:
- 1: https://tanstack.com/query/latest/docs/framework/preact/installation?utm_source=openai
- 2: https://tanstack.com/query/latest/docs/framework/preact/quick-start?utm_source=openai
- 3: https://tanstack.com/query/latest/docs/framework/preact/guides/queries?utm_source=openai
- 4: https://tanstack.com/query/latest/docs/framework/preact/reference/functions/QueryClientProvider?utm_source=openai
- 5: https://newreleases.io/project/github/TanStack/query/release/release-2026-03-23-1536?utm_source=openai
Use @tanstack/preact-query instead of @tanstack/react-query.
Line 21 imports from @tanstack/react-query, which is mismatched for this Preact guide. The correct package is @tanstack/preact-query, consistent with how other frameworks (React, Vue, Solid) each have their own TanStack Query adapter.
Suggested diff
import { useForm } from '@tanstack/preact-form'
-import { useQuery } from '@tanstack/react-query'
+import { useQuery } from '@tanstack/preact-query'📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| import { useForm } from '@tanstack/preact-form' | |
| import { useQuery } from '@tanstack/react-query' | |
| import { useForm } from '@tanstack/preact-form' | |
| import { useQuery } from '@tanstack/preact-query' | |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@docs/framework/preact/guides/async-initial-values.md` around lines 20 - 22,
The import for useQuery is using the React adapter; change the import statement
that currently reads "import { useQuery } from '@tanstack/react-query'" to use
the Preact adapter by importing from "@tanstack/preact-query" so the guide's
code uses the correct TanStack Query package for Preact (update the import where
useQuery is declared alongside useForm).
| ## React Native (via compat) | ||
|
|
||
| Because React Native doesn't have access to the DOM's `querySelectorAll` API, we need to manually manage the element list | ||
| of the inputs. This allows us to focus the first input with an error: | ||
|
|
||
| ```tsx | ||
| import { useRef } from 'preact/hooks' | ||
| import { Text, View, TextInput, Button, Alert } from 'react-native' | ||
| import { useForm } from '@tanstack/preact-form' | ||
| import { z } from 'zod' | ||
|
|
||
| export default function App() { | ||
| // This can be extracted to a hook that returns the `fields` ref, a `focusFirstField` function, and a `addField` function | ||
| const fields = useRef([] as Array<{ input: TextInput; name: string }>) | ||
|
|
||
| const form = useForm({ | ||
| defaultValues: { age: 0 }, | ||
| validators: { | ||
| onChange: z.object({ | ||
| age: z.number().min(12), | ||
| }), | ||
| }, | ||
| onSubmit() { | ||
| Alert.alert('Submitted!') | ||
| }, | ||
| onSubmitInvalid({ formApi }) { | ||
| const errorMap = formApi.state.errorMap.onChange | ||
| const inputs = fields.current | ||
|
|
||
| let firstInput | ||
| for (const input of inputs) { | ||
| if (!input || !input.input) continue | ||
| if (!!errorMap[input.name]) { | ||
| firstInput = input.input | ||
| break | ||
| } | ||
| } | ||
| firstInput?.focus() | ||
| }, | ||
| }) | ||
|
|
||
| return ( | ||
| <View style={{ padding: 16 }}> | ||
| <form.Field | ||
| name="age" | ||
| children={(field) => ( | ||
| <View style={{ marginVertical: 16 }}> | ||
| <Text>Age</Text> | ||
| <TextInput | ||
| keyboardType="numeric" | ||
| ref={(input) => { | ||
| // fields.current needs to be manually incremented so that we know what fields are rendered or not and in what order | ||
| fields.current[0] = { input, name: field.name } | ||
| }} | ||
| style={{ | ||
| borderWidth: 1, | ||
| borderColor: '#999999', | ||
| borderRadius: 4, | ||
| marginTop: 8, | ||
| padding: 8, | ||
| }} | ||
| onChangeText={(val) => field.handleChange(Number(val))} | ||
| value={field.state.value} | ||
| /> | ||
| </View> | ||
| )} | ||
| /> | ||
| <Button title="Submit" onPress={form.handleSubmit} /> | ||
| </View> | ||
| ) | ||
| } |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Check if React docs have a similar section and how it's framed
fd -t f "focus-management.md" --exec cat {}Repository: TanStack/form
Length of output: 7571
🌐 Web query:
preact/hooks react-native compatibility how does it work
💡 Result:
preact/hooks is Preact’s own hooks implementation, and it only works when your component tree is being rendered by Preact’s renderer (either directly via preact or via the React-compat layer preact/compat). It is not React’s hooks package. [1]
How “React compatibility” works (where it does work)
Preact’s React ecosystem support is primarily done by mapping React’s public API surface to Preact using preact/compat (e.g., import React from "react" can be redirected to preact/compat). This is typically done by aliasing/redirecting module resolution so packages that import react/react-dom get Preact instead. [2][1]
A common “official-ish” way to do that today is npm aliasing:
- install
reactas@preact/compat - install
react-domas@preact/compat
soreact/react-domresolve to Preact’s compat layer. [2]
Don’t mix hook implementations
If some code imports hooks from preact/hooks while other code (or dependencies) import hooks from react (which you aliased to preact/compat), you can end up with two different hook singletons and get hard-to-debug runtime errors. The practical rule: pick one import style consistently across your app and dependencies. [3]
Why this usually doesn’t work for React Native (iOS/Android)
React Native is not “React DOM”; it uses a different renderer (react-native) with its own internals and expectations. Simply aliasing react to preact/compat doesn’t give you a compatible React Native renderer, so it’s not supported out-of-the-box. [4]
If you want Preact-like size/perf in a “React Native app,” the common approaches are:
- Use React Native Web on the web side (it explicitly notes Preact can be supported there), because that path ultimately renders to the web/DOM toolchain. [5]
- Or embed a web app (React/Preact) in a WebView rather than trying to replace React inside the native renderer. [4]
Sources: Preact compat/differences docs [1], @preact/compat alias package instructions [2], report on hook-implementation mismatch [3], React Native not supported out of the box [4], React Native Web rendering notes (mentions Preact support) [5].
🏁 Script executed:
# Check if there are any other references to Preact + React Native in the codebase
rg -i "preact.*react-native|react-native.*preact" --type mdRepository: TanStack/form
Length of output: 39
🏁 Script executed:
# Look at the Preact documentation structure to understand if React Native is a supported target
fd -t f "README.md" -path "*/preact/*" --exec head -50 {}Repository: TanStack/form
Length of output: 225
🏁 Script executed:
# Check Preact/compat documentation or examples related to React Native
fd -t f "compat*" -path "*/preact/*" --exec cat {} \;Repository: TanStack/form
Length of output: 225
🏁 Script executed:
# Search for React Native in TanStack Form preact documentation
rg -i "react.native" docs/framework/preact/ -A 3 -B 3Repository: TanStack/form
Length of output: 1160
🏁 Script executed:
# Check if there's any special setup or adapter code for React Native
fd -t f "preact" --exec grep -l "react-native" {} \;Repository: TanStack/form
Length of output: 39
🏁 Script executed:
# Look for any configuration or setup guide mentioning React Native + Preact
fd -t f "*.md" docs/ --exec grep -l "react-native.*preact\|preact.*react-native" {} \;Repository: TanStack/form
Length of output: 340
🌐 Web query:
TanStack Form Preact React Native support documentation
💡 Result:
React Native support (TanStack Form)
TanStack Form’s React adapter documents React Native usage and says it “should support React Native out-of-the-box” (no special config), with examples using TextInput/onChangeText. [1]
Preact support (TanStack Form)
TanStack Form’s official “Supported Frameworks” list includes React, Vue, Angular, Solid, Lit, Svelte—not Preact. It also invites the community to create additional adapters. [2]
There was work on a Preact adapter in the TanStack/form repo (PR “PREACT ADAPTER: update all react tooling and use preact instead”), but that PR was closed (Mar 31, 2026) rather than merged. [3]
Sources:
[1] TanStack Form React docs – “Usage with React Native”
[2] TanStack Form docs – “Supported TanStack Form Frameworks”
[3] TanStack/form GitHub – PR #1880 (“PREACT ADAPTER…”)
🏁 Script executed:
# Check package.json for Preact presence
cat package.json | head -100Repository: TanStack/form
Length of output: 2831
🏁 Script executed:
# Look for supported frameworks documentation in the repo
fd -t f "supported\|frameworks\|getting-started" docs/ | head -20Repository: TanStack/form
Length of output: 39
🏁 Script executed:
# Check the main docs structure
ls -la docs/Repository: TanStack/form
Length of output: 814
🏁 Script executed:
# Search for any references to Preact adapter status or warnings
rg -i "preact.*(experimental|unsupported|closed|deprecated|adapter)" --type mdRepository: TanStack/form
Length of output: 39
🏁 Script executed:
# Check the docs config to see if Preact is listed
cat docs/config.json | jq '.'Repository: TanStack/form
Length of output: 22463
🏁 Script executed:
# Look for framework list in overview or installation docs
rg -i "supported.*framework|framework.*support" docs/ -A 5 -B 5Repository: TanStack/form
Length of output: 2696
🏁 Script executed:
# Check if there's any disclaimer about Preact being unsupported
rg -i "preact" docs/ --type md | head -30Repository: TanStack/form
Length of output: 3677
🌐 Web query:
TanStack Form Preact adapter status 2026 official support
💡 Result:
As of March 31, 2026, TanStack Form does not have an official Preact adapter. The official “Supported TanStack Form Frameworks” list includes React, Vue, Angular, Solid, Lit, and Svelte—but not Preact. (tanstack.com)
There was a “PREACT ADAPTER” pull request (#1880) proposing Preact support, but it was closed on March 31, 2026 (not merged), so it did not become officially supported. (github.com)
Citations:
- 1: https://tanstack.com/form/v1/docs/framework
- 2: PREACT ADAPTER: update all react tooling and use preact instead #1880
Remove React Native section or add critical disclaimer—Preact is not officially supported and the React Native example is non-functional.
Preact is not in TanStack Form's official supported frameworks list (React, Vue, Angular, Solid, Lit, Svelte). The Preact adapter PR was closed without merging (March 31, 2026). The "React Native (via compat)" section is particularly problematic: preact/compat provides DOM API compatibility only and does not work with React Native's renderer. Using preact/hooks with react-native components will not function as documented. Either remove this section entirely, or add a clear warning that Preact and React Native integration is unsupported and non-functional.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@docs/framework/preact/guides/focus-management.md` around lines 71 - 141,
Update the "React Native (via compat)" section in focus-management.md: either
remove the entire React Native example (the App component and its imports:
useRef from 'preact/hooks', TextInput, View, Text, Button, Alert from
'react-native', and useForm from '@tanstack/preact-form') or replace it with a
prominent warning stating that Preact is not officially supported by TanStack
Form (not listed among React/Vue/Angular/Solid/Lit/Svelte), the Preact adapter
PR was closed, and that using preact/compat or preact/hooks with react-native
components is unsupported and non-functional; ensure the warning appears before
any example and references "React Native (via compat)" and the App/example
snippet so readers cannot mistake it for a working integration.
|
|
||
| # Interface: WithFieldGroupProps\<TFieldGroupData, TFieldComponents, TFormComponents, TSubmitMeta, TRenderProps\> | ||
|
|
||
| Defined in: [packages/react-form/src/createFormHook.tsx:256](https://github.com/TanStack/form/blob/main/packages/react-form/src/createFormHook.tsx#L256) |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Description: Verify that the Preact package has the corresponding source file
# Check if the preact-form package has createFormHook.tsx
fd -t f 'createFormHook.tsx' packages/preact-form/
# Also verify the interface exists in the Preact implementation
rg -n "interface WithFieldGroupProps" packages/preact-form/ -A 2Repository: TanStack/form
Length of output: 364
🏁 Script executed:
# Check the documentation file to see all the "Defined in:" references
cat docs/framework/preact/reference/interfaces/WithFieldGroupProps.md | head -60 | cat -nRepository: TanStack/form
Length of output: 1688
🏁 Script executed:
# Get the lines around 257, 264, and 265 in the Preact file to understand what they define
sed -n '255,266p' packages/preact-form/src/createFormHook.tsx | cat -nRepository: TanStack/form
Length of output: 578
🏁 Script executed:
# Check what the React version has at similar lines for comparison
rg -n "interface WithFieldGroupProps" packages/react-form/src/ -A 2Repository: TanStack/form
Length of output: 317
Fix incorrect source file references in Preact documentation.
This documentation file for Preact references packages/react-form/src/createFormHook.tsx instead of the correct packages/preact-form/src/createFormHook.tsx. Update the "Defined in:" links on lines 8, 44, and 54 to point to the Preact implementation.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@docs/framework/preact/reference/interfaces/WithFieldGroupProps.md` at line 8,
The "Defined in:" links in
docs/framework/preact/reference/interfaces/WithFieldGroupProps.md incorrectly
point to the React implementation; update the three links (currently referencing
packages/react-form/src/createFormHook.tsx) to reference the Preact
implementation at packages/preact-form/src/createFormHook.tsx so the
documentation for WithFieldGroupProps correctly links to the Preact source
(adjust the links on the lines that show "Defined in:" and any other occurrences
that reference createFormHook.tsx).
| }) => ReturnType<FunctionComponent>; | ||
| ``` | ||
|
|
||
| Defined in: [packages/react-form/src/useField.tsx:523](https://github.com/TanStack/form/blob/main/packages/react-form/src/useField.tsx#L523) |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Description: Verify that the Preact package has the corresponding source file
# Check if the preact-form package has useField.tsx
fd -t f 'useField.tsx' packages/preact-form/
# Verify the type alias exists in the Preact implementation
rg -n "type FieldComponent" packages/preact-form/ -A 2Repository: TanStack/form
Length of output: 343
Incorrect source file reference for Preact documentation.
The "Defined in:" link on line 15 references packages/react-form/src/useField.tsx:523, but this is Preact documentation and should reference packages/preact-form/src/useField.tsx:521 instead.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@docs/framework/preact/reference/type-aliases/FieldComponent.md` at line 15,
In docs/framework/preact/reference/type-aliases/FieldComponent.md update the
"Defined in:" link so it points to the Preact implementation of useField (the
preact-form useField.tsx source) instead of the React implementation; replace
the react-form reference with the package that contains the Preact useField
definition so the link resolves to the correct Preact source location.
| --- | ||
| id: ReactFormExtendedApi | ||
| title: ReactFormExtendedApi | ||
| --- | ||
|
|
||
| # Type Alias: ReactFormExtendedApi\<TFormData, TOnMount, TOnChange, TOnChangeAsync, TOnBlur, TOnBlurAsync, TOnSubmit, TOnSubmitAsync, TOnDynamic, TOnDynamicAsync, TOnServer, TSubmitMeta\> | ||
|
|
||
| ```ts | ||
| type ReactFormExtendedApi<TFormData, TOnMount, TOnChange, TOnChangeAsync, TOnBlur, TOnBlurAsync, TOnSubmit, TOnSubmitAsync, TOnDynamic, TOnDynamicAsync, TOnServer, TSubmitMeta> = FormApi<TFormData, TOnMount, TOnChange, TOnChangeAsync, TOnBlur, TOnBlurAsync, TOnSubmit, TOnSubmitAsync, TOnDynamic, TOnDynamicAsync, TOnServer, TSubmitMeta> & ReactFormApi<TFormData, TOnMount, TOnChange, TOnChangeAsync, TOnBlur, TOnBlurAsync, TOnSubmit, TOnSubmitAsync, TOnDynamic, TOnDynamicAsync, TOnServer, TSubmitMeta>; | ||
| ``` | ||
|
|
||
| Defined in: [packages/react-form/src/useForm.tsx:99](https://github.com/TanStack/form/blob/main/packages/react-form/src/useForm.tsx#L99) | ||
|
|
||
| An extended version of the `FormApi` class that includes React-specific functionalities from `ReactFormApi` |
There was a problem hiding this comment.
Documentation incorrectly references React instead of Preact.
This documentation file for the Preact package incorrectly references React throughout:
- The type alias is named
ReactFormExtendedApiinstead ofPreactFormExtendedApi - The source definition points to
packages/react-form/src/useForm.tsxinstead ofpackages/preact-form/src/useForm.tsx - The description mentions "React-specific functionalities"
This appears to be a copy-paste from the React documentation that wasn't updated for Preact. Should either be renamed to use Preact-specific naming or, if the Preact package intentionally reuses React type names for API consistency, the source path and description should still be corrected.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@docs/framework/preact/reference/type-aliases/ReactFormExtendedApi.md` around
lines 1 - 14, The docs incorrectly reference React; update the type alias and
metadata to Preact equivalents: rename ReactFormExtendedApi to
PreactFormExtendedApi in this doc, update the source link to
packages/preact-form/src/useForm.tsx (and confirm the definition in
useForm.tsx), and change the description text to mention Preact-specific
functionalities (or explicitly note that the Preact package reuses ReactFormApi
naming if that is intentional). Ensure references to ReactFormApi/FormApi remain
accurate or are renamed to PreactFormApi/FormApi to match the actual exported
types in the preact package.
| // We should never hit the `null` case here | ||
| const FieldContext = createContext<AnyFieldApi>(null as never) | ||
| const FormContext = createContext<AnyFormApi>(null as never) |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
# First, let's locate and examine the file
find . -path "*packages/preact-form/src/createFormHook.tsx" -type fRepository: TanStack/form
Length of output: 102
🏁 Script executed:
# Let's view the entire file to understand the structure
cat -n packages/preact-form/src/createFormHook.tsxRepository: TanStack/form
Length of output: 21514
createFormHookContexts() and useTypedAppFormContext() break context isolation between independent hook instances.
FieldContext and FormContext are created once at module scope (lines 23–25). Each call to createFormHookContexts() returns references to these shared singletons, not isolated instances. Additionally, useTypedAppFormContext() (line 592) ignores the formContext parameter passed to createFormHook() and reads from the module-scoped FormContext instead. This prevents proper isolation when creating multiple independent form systems with separate contexts.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@packages/preact-form/src/createFormHook.tsx` around lines 23 - 25, The
module-scoped FieldContext and FormContext singletons break isolation: instead,
create new contexts inside createFormHookContexts() (call
createContext<AnyFieldApi>() and createContext<AnyFormApi>() there) and return
those instances so each hook system gets its own contexts; then update
useTypedAppFormContext to accept or close over the form context returned from
createFormHookContexts() (stop reading the module-scoped FormContext) so it uses
the instance-specific FormContext/FieldContext. Ensure all references to the
former module-scoped FieldContext/FormContext (and any functions that call
useTypedAppFormContext) are updated to use the contexts returned by
createFormHookContexts().
| UnwrapDefaultOrAny<undefined | FormValidateOrFn<TFormData>, TOnMount>, | ||
| UnwrapDefaultOrAny<undefined | FormValidateOrFn<TFormData>, TOnChange>, | ||
| UnwrapDefaultOrAny<undefined | FormValidateOrFn<TFormData>, TOnChangeAsync>, | ||
| UnwrapDefaultOrAny<undefined | FormValidateOrFn<TFormData>, TOnBlur>, | ||
| UnwrapDefaultOrAny<undefined | FormValidateOrFn<TFormData>, TOnBlurAsync>, | ||
| UnwrapDefaultOrAny<undefined | FormValidateOrFn<TFormData>, TOnSubmit>, | ||
| UnwrapDefaultOrAny<undefined | FormValidateOrFn<TFormData>, TOnSubmitAsync>, | ||
| UnwrapDefaultOrAny<undefined | FormValidateOrFn<TFormData>, TOnDynamic>, | ||
| UnwrapDefaultOrAny< | ||
| undefined | FormValidateOrFn<TFormData>, | ||
| TOnDynamicAsync | ||
| >, | ||
| UnwrapDefaultOrAny<undefined | FormValidateOrFn<TFormData>, TOnServer>, |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
head -n 450 packages/preact-form/src/createFormHook.tsx | tail -n 30Repository: TanStack/form
Length of output: 1217
🏁 Script executed:
rg "FormValidateOrFn|FormAsyncValidateOrFn" packages/preact-form/src/ -A 3Repository: TanStack/form
Length of output: 31003
🏁 Script executed:
rg "UnwrapDefaultOrAny" packages/preact-form/src/ -B 2 -A 5Repository: TanStack/form
Length of output: 2447
Use FormAsyncValidateOrFn for async validator slots in type narrowing.
The UnwrapDefaultOrAny branches for TOnChangeAsync, TOnBlurAsync, TOnSubmitAsync, TOnDynamicAsync, and TOnServer must use FormAsyncValidateOrFn instead of FormValidateOrFn to match their actual type parameter defaults. Currently, the mismatch prevents UnwrapDefaultOrAny from detecting when these generics use their defaults, causing improper type narrowing in withForm.
Fix
- UnwrapDefaultOrAny<undefined | FormValidateOrFn<TFormData>, TOnChangeAsync>,
+ UnwrapDefaultOrAny<
+ undefined | FormAsyncValidateOrFn<TFormData>,
+ TOnChangeAsync
+ >,
@@
- UnwrapDefaultOrAny<undefined | FormValidateOrFn<TFormData>, TOnBlurAsync>,
+ UnwrapDefaultOrAny<
+ undefined | FormAsyncValidateOrFn<TFormData>,
+ TOnBlurAsync
+ >,
@@
- UnwrapDefaultOrAny<undefined | FormValidateOrFn<TFormData>, TOnSubmitAsync>,
+ UnwrapDefaultOrAny<
+ undefined | FormAsyncValidateOrFn<TFormData>,
+ TOnSubmitAsync
+ >,
@@
- UnwrapDefaultOrAny<
- undefined | FormValidateOrFn<TFormData>,
- TOnDynamicAsync
- >,
- UnwrapDefaultOrAny<undefined | FormValidateOrFn<TFormData>, TOnServer>,
+ UnwrapDefaultOrAny<
+ undefined | FormAsyncValidateOrFn<TFormData>,
+ TOnDynamicAsync
+ >,
+ UnwrapDefaultOrAny<
+ undefined | FormAsyncValidateOrFn<TFormData>,
+ TOnServer
+ >,📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| UnwrapDefaultOrAny<undefined | FormValidateOrFn<TFormData>, TOnMount>, | |
| UnwrapDefaultOrAny<undefined | FormValidateOrFn<TFormData>, TOnChange>, | |
| UnwrapDefaultOrAny<undefined | FormValidateOrFn<TFormData>, TOnChangeAsync>, | |
| UnwrapDefaultOrAny<undefined | FormValidateOrFn<TFormData>, TOnBlur>, | |
| UnwrapDefaultOrAny<undefined | FormValidateOrFn<TFormData>, TOnBlurAsync>, | |
| UnwrapDefaultOrAny<undefined | FormValidateOrFn<TFormData>, TOnSubmit>, | |
| UnwrapDefaultOrAny<undefined | FormValidateOrFn<TFormData>, TOnSubmitAsync>, | |
| UnwrapDefaultOrAny<undefined | FormValidateOrFn<TFormData>, TOnDynamic>, | |
| UnwrapDefaultOrAny< | |
| undefined | FormValidateOrFn<TFormData>, | |
| TOnDynamicAsync | |
| >, | |
| UnwrapDefaultOrAny<undefined | FormValidateOrFn<TFormData>, TOnServer>, | |
| UnwrapDefaultOrAny<undefined | FormValidateOrFn<TFormData>, TOnMount>, | |
| UnwrapDefaultOrAny<undefined | FormValidateOrFn<TFormData>, TOnChange>, | |
| UnwrapDefaultOrAny< | |
| undefined | FormAsyncValidateOrFn<TFormData>, | |
| TOnChangeAsync | |
| >, | |
| UnwrapDefaultOrAny<undefined | FormValidateOrFn<TFormData>, TOnBlur>, | |
| UnwrapDefaultOrAny< | |
| undefined | FormAsyncValidateOrFn<TFormData>, | |
| TOnBlurAsync | |
| >, | |
| UnwrapDefaultOrAny<undefined | FormValidateOrFn<TFormData>, TOnSubmit>, | |
| UnwrapDefaultOrAny< | |
| undefined | FormAsyncValidateOrFn<TFormData>, | |
| TOnSubmitAsync | |
| >, | |
| UnwrapDefaultOrAny<undefined | FormValidateOrFn<TFormData>, TOnDynamic>, | |
| UnwrapDefaultOrAny< | |
| undefined | FormAsyncValidateOrFn<TFormData>, | |
| TOnDynamicAsync | |
| >, | |
| UnwrapDefaultOrAny< | |
| undefined | FormAsyncValidateOrFn<TFormData>, | |
| TOnServer | |
| >, |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@packages/preact-form/src/createFormHook.tsx` around lines 433 - 445, The
type-narrowing mismatch is caused by using FormValidateOrFn for async validator
slots; update the UnwrapDefaultOrAny generic instantiations for TOnChangeAsync,
TOnBlurAsync, TOnSubmitAsync, TOnDynamicAsync and TOnServer to use
FormAsyncValidateOrFn (instead of FormValidateOrFn) so the default-detection
logic in UnwrapDefaultOrAny correctly recognizes the async defaults used by
withForm; touch the occurrences in createFormHook.tsx where UnwrapDefaultOrAny
is invoked with these TOn*Async type parameters.
| const [prevFormId, setPrevFormId] = useState<string>(opts?.formId as never) | ||
|
|
||
| const [formApi, setFormApi] = useState(() => { | ||
| return new FormApi< | ||
| TFormData, | ||
| TOnMount, | ||
| TOnChange, | ||
| TOnChangeAsync, | ||
| TOnBlur, | ||
| TOnBlurAsync, | ||
| TOnSubmit, | ||
| TOnSubmitAsync, | ||
| TOnDynamic, | ||
| TOnDynamicAsync, | ||
| TOnServer, | ||
| TSubmitMeta | ||
| >({ ...opts, formId: opts?.formId ?? fallbackFormId }) | ||
| }) | ||
|
|
||
| if (prevFormId !== opts?.formId) { | ||
| const formId = opts?.formId ?? fallbackFormId | ||
| setFormApi(new FormApi({ ...opts, formId })) | ||
| setPrevFormId(formId) | ||
| } |
There was a problem hiding this comment.
Swapping FormApi on formId change leaves the replacement instance unmounted.
When formId changes after the first commit, Lines 207-211 replace formApi, but Line 258 only mounts the original instance because the effect is pinned to []. The new FormApi never gets mount(), and the old one stays mounted until unmount. Please key the lifecycle off the current instance/resolved formId instead of replacing it only in render.
Also applies to: 258-258
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@packages/preact-form/src/useForm.tsx` around lines 188 - 211, The current
render-time replacement of formApi (via setFormApi and setPrevFormId) can create
an instance that never receives mount() because the mounting effect is pinned to
[] — instead resolve the current formId (opts?.formId ?? fallbackFormId) and
move creation/teardown of FormApi into a useEffect that depends on that resolved
formId (and/or the formApi instance) so the effect will call new FormApi(...),
call mount() on the new instance, and unmount() the previous instance; update
references to prevFormId/setPrevFormId to no longer swap instances in render and
ensure FormApi lifecycle is keyed by the resolved formId and the formApi
instance.
There was a problem hiding this comment.
Actionable comments posted: 4
♻️ Duplicate comments (1)
packages/preact-form/src/useForm.tsx (1)
187-211:⚠️ Potential issue | 🔴 CriticalResolve
formIdonce before comparing or mounting theFormApi.
prevFormIdis updated with the resolved id, but the guard compares it to rawopts?.formId. If a caller goes from"custom-id"back toundefined, the check stays true forever (fallbackId !== undefined), so render keeps creating newFormApiinstances; and with Line 258 pinned to[], replacements still never getmount().💡 Minimal fix
const fallbackFormId = useFormId() - const [prevFormId, setPrevFormId] = useState<string>(opts?.formId as never) + const resolvedFormId = opts?.formId ?? fallbackFormId + const [prevFormId, setPrevFormId] = useState<string>(resolvedFormId) const [formApi, setFormApi] = useState(() => { - return new FormApi< - TFormData, - TOnMount, - TOnChange, - TOnChangeAsync, - TOnBlur, - TOnBlurAsync, - TOnSubmit, - TOnSubmitAsync, - TOnDynamic, - TOnDynamicAsync, - TOnServer, - TSubmitMeta - >({ ...opts, formId: opts?.formId ?? fallbackFormId }) + return new FormApi< + TFormData, + TOnMount, + TOnChange, + TOnChangeAsync, + TOnBlur, + TOnBlurAsync, + TOnSubmit, + TOnSubmitAsync, + TOnDynamic, + TOnDynamicAsync, + TOnServer, + TSubmitMeta + >({ ...opts, formId: resolvedFormId }) }) - if (prevFormId !== opts?.formId) { - const formId = opts?.formId ?? fallbackFormId - setFormApi(new FormApi({ ...opts, formId })) - setPrevFormId(formId) + if (prevFormId !== resolvedFormId) { + setFormApi(new FormApi({ ...opts, formId: resolvedFormId })) + setPrevFormId(resolvedFormId) } … - useIsomorphicLayoutEffect(formApi.mount, []) + useIsomorphicLayoutEffect(formApi.mount, [formApi])Also applies to: 258-258
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/preact-form/src/useForm.tsx` around lines 187 - 211, Resolve the effective formId once (const resolvedFormId = opts?.formId ?? fallbackFormId) and use that when initializing FormApi and when comparing/updating prevFormId; initialize prevFormId to resolvedFormId (not raw opts?.formId), compare prevFormId !== resolvedFormId before creating a new FormApi, call setFormApi(new FormApi({ ...opts, formId: resolvedFormId })) and then setPrevFormId(resolvedFormId) so switching between a custom id and undefined (which falls back) won’t endlessly recreate FormApi or block mount().
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@docs/framework/angular/guides/dynamic-validation.md`:
- Around line 13-17: These Angular documentation formatting edits (changes to
imports like TanStackField, injectForm, revalidateLogic in the dynamic
validation docs) are unrelated to the Preact integration PR—remove or revert
these formatting-only changes from this branch and instead place them in a
separate doc/formatting PR; specifically undo the modified import formatting and
any other changes around the same import blocks (also at the other noted
regions) so the current PR only contains Preact integration code.
- Line 23: The placeholder template strings (e.g., the property template: ` <!--
Your form template here --> `) removed the real Angular form examples; restore
the original working template HTML wherever those placeholders appear (including
occurrences at the three other spots referenced) by replacing the placeholder
string with the original form markup used elsewhere in the doc (copy the
concrete template examples from the retained examples around the other sections
such as the form controls and validation message snippets) so each example
includes full, runnable template code; update the same template property
name/variable where it was replaced to ensure consistency across examples.
In `@packages/preact-form/src/useField.tsx`:
- Around line 34-49: The Field member on the field API is currently assigned the
unbound root Field component; instead bind and expose a wrapper that injects the
current field's form context so consumers can call fieldApi.Field(...) without
manually providing form. Replace the direct assignment of the root Field with a
bound component factory that captures the parent form (the same form used by
useField) and renders the root Field passing form plus any incoming props;
reference the Field symbol, the fieldApi.Field member, and the useField call in
the root Field component to ensure the wrapper forwards form to useField.
In `@packages/preact-form/src/useFieldGroup.tsx`:
- Around line 169-254: useFieldGroup currently creates formLensApi once and
never syncs it with changing inputs; add an update path so wrappers
(extendedApi.AppField, Field, Subscribe) don't hold stale refs. Fix by either
adding an update(opts) method to FieldGroupApi (matching FieldApi/FormApi) and
calling formLensApi.update(opts) on each render (or inside a useEffect) to
propagate changes to opts.form, opts.fields, opts.defaultValues,
opts.onSubmitMeta, and opts.formComponents, or recreate the FieldGroupApi
instance when those specific binding inputs change (replace the useState factory
with a keyed memo/rehydration that re-instantiates FieldGroupApi when
opts.form/fields/defaultValues/onSubmitMeta/formComponents change); ensure the
same extendedApi properties (AppField/AppForm/Field/Subscribe) continue to
reference the updated internal form instance.
---
Duplicate comments:
In `@packages/preact-form/src/useForm.tsx`:
- Around line 187-211: Resolve the effective formId once (const resolvedFormId =
opts?.formId ?? fallbackFormId) and use that when initializing FormApi and when
comparing/updating prevFormId; initialize prevFormId to resolvedFormId (not raw
opts?.formId), compare prevFormId !== resolvedFormId before creating a new
FormApi, call setFormApi(new FormApi({ ...opts, formId: resolvedFormId })) and
then setPrevFormId(resolvedFormId) so switching between a custom id and
undefined (which falls back) won’t endlessly recreate FormApi or block mount().
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 4d4bcb03-a51f-410d-bcfe-8db67e5142cc
📒 Files selected for processing (15)
docs/framework/angular/guides/arrays.mddocs/framework/angular/guides/basic-concepts.mddocs/framework/angular/guides/dynamic-validation.mddocs/framework/angular/guides/form-composition.mddocs/framework/angular/guides/listeners.mddocs/framework/angular/guides/submission-handling.mddocs/framework/angular/guides/validation.mddocs/framework/angular/quick-start.mdpackages/preact-form/src/index.tspackages/preact-form/src/useField.tsxpackages/preact-form/src/useFieldGroup.tsxpackages/preact-form/src/useForm.tsxpackages/preact-form/src/useStore.tspackages/preact-form/tests/useField.test.tsxpackages/preact-form/tests/useForm.test.tsx
✅ Files skipped from review due to trivial changes (9)
- docs/framework/angular/guides/listeners.md
- docs/framework/angular/quick-start.md
- docs/framework/angular/guides/form-composition.md
- docs/framework/angular/guides/basic-concepts.md
- docs/framework/angular/guides/submission-handling.md
- docs/framework/angular/guides/arrays.md
- packages/preact-form/src/index.ts
- docs/framework/angular/guides/validation.md
- packages/preact-form/tests/useField.test.tsx
| import { | ||
| TanStackField, | ||
| injectForm, | ||
| revalidateLogic, | ||
| } from '@tanstack/angular-form' |
There was a problem hiding this comment.
These Angular documentation formatting changes are unrelated to the PR objective.
This PR is about adding Preact integration, but these changes modify Angular documentation formatting. Consider moving unrelated documentation formatting updates to a separate PR to maintain clear change scope and simplify review.
Also applies to: 90-95, 127-132, 178-182, 244-248, 283-287
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@docs/framework/angular/guides/dynamic-validation.md` around lines 13 - 17,
These Angular documentation formatting edits (changes to imports like
TanStackField, injectForm, revalidateLogic in the dynamic validation docs) are
unrelated to the Preact integration PR—remove or revert these formatting-only
changes from this branch and instead place them in a separate doc/formatting PR;
specifically undo the modified import formatting and any other changes around
the same import blocks (also at the other noted regions) so the current PR only
contains Preact integration code.
| template: ` | ||
| <!-- Your form template here --> | ||
| `, | ||
| template: ` <!-- Your form template here --> `, |
There was a problem hiding this comment.
Documentation regression: Template examples replaced with non-functional placeholders.
Replacing actual template code with placeholder comments like <!-- Your form template here --> significantly reduces the educational value of this guide. Users rely on complete, working examples to understand how to implement dynamic validation.
Additionally, this is inconsistent—some examples (e.g., lines 101, 138-143, 189-213) retain actual template code while these are replaced with placeholders.
Restore the original template examples to maintain documentation quality.
Also applies to: 70-70, 254-254, 294-294
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@docs/framework/angular/guides/dynamic-validation.md` at line 23, The
placeholder template strings (e.g., the property template: ` <!-- Your form
template here --> `) removed the real Angular form examples; restore the
original working template HTML wherever those placeholders appear (including
occurrences at the three other spots referenced) by replacing the placeholder
string with the original form markup used elsewhere in the doc (copy the
concrete template examples from the retained examples around the other sections
such as the form controls and validation message snippets) so each example
includes full, runnable template code; update the same template property
name/variable where it was replaced to ensure consistency across examples.
| * A pre-bound and type-safe sub-field component using this field as a root. | ||
| */ | ||
| Field: FieldComponent< | ||
| TParentData, | ||
| TFormOnMount, | ||
| TFormOnChange, | ||
| TFormOnChangeAsync, | ||
| TFormOnBlur, | ||
| TFormOnBlurAsync, | ||
| TFormOnSubmit, | ||
| TFormOnSubmitAsync, | ||
| TFormOnDynamic, | ||
| TFormOnDynamicAsync, | ||
| TFormOnServer, | ||
| TPatentSubmitMeta | ||
| > |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
cd packages/preact-form && wc -l src/useField.tsxRepository: TanStack/form
Length of output: 77
🏁 Script executed:
cat -n packages/preact-form/src/useField.tsx | sed -n '30,55p'Repository: TanStack/form
Length of output: 945
🏁 Script executed:
cat -n packages/preact-form/src/useField.tsx | sed -n '315,330p'Repository: TanStack/form
Length of output: 512
🏁 Script executed:
cat -n packages/preact-form/src/useField.tsx | sed -n '715,750p'Repository: TanStack/form
Length of output: 1113
🏁 Script executed:
cat -n packages/preact-form/src/useField.tsx | sed -n '200,230p'Repository: TanStack/form
Length of output: 1237
🏁 Script executed:
cat -n packages/preact-form/src/useField.tsx | sed -n '280,325p'Repository: TanStack/form
Length of output: 1314
🏁 Script executed:
cat -n packages/preact-form/src/useField.tsx | sed -n '240,280p'Repository: TanStack/form
Length of output: 1764
🏁 Script executed:
cat -n packages/preact-form/src/useField.tsx | sed -n '150,180p'Repository: TanStack/form
Length of output: 1542
🏁 Script executed:
rg "fieldApi\.Field|field\.Field" packages/preact-form --type ts --type tsx -B 2 -A 2Repository: TanStack/form
Length of output: 84
🏁 Script executed:
grep -r "pre-bound" packages/preact-form --include="*.ts" --include="*.tsx" -B 2 -A 2Repository: TanStack/form
Length of output: 366
🏁 Script executed:
rg "fieldApi\.Field|field\.Field" packages/preact-form -B 2 -A 2Repository: TanStack/form
Length of output: 39
🏁 Script executed:
find packages/preact-form -name "*.test.*" -o -name "*.spec.*" | head -5Repository: TanStack/form
Length of output: 196
🏁 Script executed:
grep -n "\.Field" packages/preact-form/tests/useField.test.tsx -B 2 -A 2Repository: TanStack/form
Length of output: 7833
🏁 Script executed:
cat -n packages/preact-form/src/useField.tsx | sed -n '690,750p'Repository: TanStack/form
Length of output: 2629
🏁 Script executed:
cat -n packages/preact-form/src/useField.tsx | sed -n '85,110p'Repository: TanStack/form
Length of output: 774
🏁 Script executed:
grep -n "opts\." packages/preact-form/src/useField.tsx | head -20Repository: TanStack/form
Length of output: 378
🏁 Script executed:
grep -n "field\.Field\|fieldApi\.Field" packages/preact-form/tests/ -rRepository: TanStack/form
Length of output: 39
🏁 Script executed:
cat -n packages/preact-form/src/useField.tsx | sed -n '1,50p'Repository: TanStack/form
Length of output: 2119
🏁 Script executed:
grep -n "UseFieldOptions\|UseFieldOptionsBound" packages/preact-form/src/types.ts -A 10 | head -40Repository: TanStack/form
Length of output: 1104
🏁 Script executed:
cat -n packages/preact-form/src/types.ts | sed -n '1,100p'Repository: TanStack/form
Length of output: 4114
🏁 Script executed:
cat -n packages/preact-form/src/useField.tsx | sed -n '82,135p'Repository: TanStack/form
Length of output: 1563
🏁 Script executed:
cat -n packages/preact-form/src/useField.tsx | sed -n '61,75p'Repository: TanStack/form
Length of output: 1033
🏁 Script executed:
cat -n packages/preact-form/src/useField.tsx | sed -n '136,200p'Repository: TanStack/form
Length of output: 2806
🏁 Script executed:
grep -n "form\?" packages/preact-form/src/types.ts | head -20Repository: TanStack/form
Length of output: 88
🏁 Script executed:
rg "interface UseFieldOptions" packages/preact-form/src/types.ts -A 25Repository: TanStack/form
Length of output: 2304
🏁 Script executed:
cat -n packages/preact-form/src/types.ts | sed -n '82,130p'Repository: TanStack/form
Length of output: 1800
🏁 Script executed:
cat -n packages/preact-form/tests/useField.test.tsx | sed -n '520,560p'Repository: TanStack/form
Length of output: 1996
🏁 Script executed:
python3 << 'EOF'
# Look at the test to understand the pattern
with open('packages/preact-form/tests/useField.test.tsx', 'r') as f:
content = f.read()
# Find usage of field.Field (nested field)
if 'field.Field' in content:
print("Found field.Field usage in tests")
# Extract context
idx = content.find('field.Field')
print(content[max(0, idx-200):idx+300])
else:
print("No field.Field usage found in tests")
EOFRepository: TanStack/form
Length of output: 92
🏁 Script executed:
cat -n packages/preact-form/src/useField.tsx | sed -n '256,325p'Repository: TanStack/form
Length of output: 2475
Bind fieldApi.Field instead of aliasing the unbound Field component.
The contract (line 34) documents this member as "pre-bound," but line 322 directly assigns the unbound root Field component. When users call <fieldApi.Field name="subfield">, it reaches the root component at line 746 which immediately calls useField(fieldOptions as any) without the parent field's form context. Since consumers wouldn't know to manually inject form, this breaks the API contract.
Suggested fix
Wrap the component to inject the bound form:
- extendedApi.Field = Field as never
+ extendedApi.Field = function BoundField(props) {
+ return <Field {...(props as any)} form={opts.form} />
+ } as never📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| * A pre-bound and type-safe sub-field component using this field as a root. | |
| */ | |
| Field: FieldComponent< | |
| TParentData, | |
| TFormOnMount, | |
| TFormOnChange, | |
| TFormOnChangeAsync, | |
| TFormOnBlur, | |
| TFormOnBlurAsync, | |
| TFormOnSubmit, | |
| TFormOnSubmitAsync, | |
| TFormOnDynamic, | |
| TFormOnDynamicAsync, | |
| TFormOnServer, | |
| TPatentSubmitMeta | |
| > | |
| extendedApi.Field = function BoundField(props) { | |
| return <Field {...(props as any)} form={opts.form} /> | |
| } as never |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@packages/preact-form/src/useField.tsx` around lines 34 - 49, The Field member
on the field API is currently assigned the unbound root Field component; instead
bind and expose a wrapper that injects the current field's form context so
consumers can call fieldApi.Field(...) without manually providing form. Replace
the direct assignment of the root Field with a bound component factory that
captures the parent form (the same form used by useField) and renders the root
Field passing form plus any incoming props; reference the Field symbol, the
fieldApi.Field member, and the useField call in the root Field component to
ensure the wrapper forwards form to useField.
| const [formLensApi] = useState(() => { | ||
| const api = new FieldGroupApi(opts) | ||
| const form = | ||
| opts.form instanceof FieldGroupApi | ||
| ? (opts.form.form as AppFieldExtendedReactFormApi< | ||
| TFormData, | ||
| TOnMount, | ||
| TOnChange, | ||
| TOnChangeAsync, | ||
| TOnBlur, | ||
| TOnBlurAsync, | ||
| TOnSubmit, | ||
| TOnSubmitAsync, | ||
| TOnDynamic, | ||
| TOnDynamicAsync, | ||
| TOnServer, | ||
| TSubmitMeta, | ||
| TComponents, | ||
| TFormComponents | ||
| >) | ||
| : opts.form | ||
|
|
||
| const extendedApi: AppFieldExtendedReactFieldGroupApi< | ||
| TFormData, | ||
| TFieldGroupData, | ||
| TFields, | ||
| TOnMount, | ||
| TOnChange, | ||
| TOnChangeAsync, | ||
| TOnBlur, | ||
| TOnBlurAsync, | ||
| TOnSubmit, | ||
| TOnSubmitAsync, | ||
| TOnDynamic, | ||
| TOnDynamicAsync, | ||
| TOnServer, | ||
| TSubmitMeta, | ||
| TComponents, | ||
| TFormComponents | ||
| > = api as never | ||
|
|
||
| extendedApi.AppForm = function AppForm(appFormProps) { | ||
| return <form.AppForm {...appFormProps} /> | ||
| } | ||
|
|
||
| extendedApi.AppField = function AppField(props) { | ||
| return ( | ||
| <form.AppField {...(formLensApi.getFormFieldOptions(props) as any)} /> | ||
| ) | ||
| } | ||
|
|
||
| extendedApi.Field = function Field(props) { | ||
| return <form.Field {...(formLensApi.getFormFieldOptions(props) as any)} /> | ||
| } | ||
|
|
||
| extendedApi.Subscribe = function Subscribe(props: any) { | ||
| return ( | ||
| <LocalSubscribe | ||
| lens={formLensApi} | ||
| selector={props.selector} | ||
| children={props.children} | ||
| /> | ||
| ) | ||
| } | ||
|
|
||
| return Object.assign(extendedApi, { | ||
| ...opts.formComponents, | ||
| }) as AppFieldExtendedReactFieldGroupApi< | ||
| TFormData, | ||
| TFieldGroupData, | ||
| TFields, | ||
| TOnMount, | ||
| TOnChange, | ||
| TOnChangeAsync, | ||
| TOnBlur, | ||
| TOnBlurAsync, | ||
| TOnSubmit, | ||
| TOnSubmitAsync, | ||
| TOnDynamic, | ||
| TOnDynamicAsync, | ||
| TOnServer, | ||
| TSubmitMeta, | ||
| TComponents, | ||
| TFormComponents | ||
| > | ||
| }) |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
# First, let's see the full useFieldGroup implementation
wc -l packages/preact-form/src/useFieldGroup.tsxRepository: TanStack/form
Length of output: 103
🏁 Script executed:
# Read the full useFieldGroup file to understand the context
cat -n packages/preact-form/src/useFieldGroup.tsxRepository: TanStack/form
Length of output: 9273
🏁 Script executed:
# Search for useField and useForm patterns to compare
fd -e tsx -e ts packages/preact-form/src | grep -E "(useField|useForm)" | head -20Repository: TanStack/form
Length of output: 458
🏁 Script executed:
# Look for FieldGroupApi implementation to understand if it has update methods
fd -e tsx -e ts packages/preact-form/src | xargs grep -l "class FieldGroupApi\|export.*FieldGroupApi" | head -5Repository: TanStack/form
Length of output: 458
🏁 Script executed:
# Find useField and useForm implementations for comparison
fd . 'packages/preact-form/src' | grep -E "(useField|useForm)" | head -20Repository: TanStack/form
Length of output: 212
🏁 Script executed:
# Search for FieldGroupApi definition in form-core
fd . 'packages/form-core' | grep -E "(FieldGroupApi|field-group)" | head -20Repository: TanStack/form
Length of output: 192
🏁 Script executed:
# Check the FieldGroupApi class implementation
rg "class FieldGroupApi|export.*class.*FieldGroupApi" -A 30 packages/form-core/srcRepository: TanStack/form
Length of output: 2550
🏁 Script executed:
# Get the full FieldGroupApi implementation
cat -n packages/form-core/src/FieldGroupApi.ts | head -150Repository: TanStack/form
Length of output: 5135
🏁 Script executed:
# Look for mount, update, or refresh methods in FieldGroupApi
rg "mount|update|refresh" packages/form-core/src/FieldGroupApi.ts | head -30Repository: TanStack/form
Length of output: 354
🏁 Script executed:
# Read useField implementation for comparison
cat -n packages/preact-form/src/useField.tsx | head -100Repository: TanStack/form
Length of output: 4523
🏁 Script executed:
# Read useForm implementation for comparison
cat -n packages/preact-form/src/useForm.tsx | head -100Repository: TanStack/form
Length of output: 3909
🏁 Script executed:
# Check useField full implementation to see dependency handling
wc -l packages/preact-form/src/useField.tsxRepository: TanStack/form
Length of output: 98
🏁 Script executed:
# Look for the useMemo and dependency handling in useField
rg "useMemo|useState|useIsomorphicLayoutEffect" packages/preact-form/src/useField.tsx -B 2 -A 5 | head -80Repository: TanStack/form
Length of output: 1589
🏁 Script executed:
# Check useForm full implementation to see how it handles dependency updates
wc -l packages/preact-form/src/useForm.tsxRepository: TanStack/form
Length of output: 97
🏁 Script executed:
# Look for dependency arrays in useForm
rg "useMemo|useState|useIsomorphicLayoutEffect" packages/preact-form/src/useForm.tsx -B 2 -A 5 | head -100Repository: TanStack/form
Length of output: 1486
🏁 Script executed:
# Look at FieldGroupApi constructor to see what it stores
rg "constructor" packages/form-core/src/FieldGroupApi.ts -A 30 | head -60Repository: TanStack/form
Length of output: 926
🏁 Script executed:
# Verify FieldGroupApi has an update method
rg "update\(" packages/form-core/src/FieldGroupApi.ts -B 2 -A 8 | head -40Repository: TanStack/form
Length of output: 39
🏁 Script executed:
# Check the full mount method and any other relevant methods
rg "mount\(\)|update\(" packages/form-core/src/FieldGroupApi.ts -B 1 -A 5Repository: TanStack/form
Length of output: 39
🏁 Script executed:
# Get the entire FieldGroupApi.ts file to see all methods
cat -n packages/form-core/src/FieldGroupApi.ts | tail -150Repository: TanStack/form
Length of output: 5029
🏁 Script executed:
# Search for all methods in FieldGroupApi
rg "^\s+(mount|update|getFormFieldName|getFormFieldOptions)" packages/form-core/src/FieldGroupApi.ts -A 3Repository: TanStack/form
Length of output: 665
🏁 Script executed:
# Look for the method definitions differently
ast-grep --pattern 'class FieldGroupApi { $$$ }' packages/form-core/src/FieldGroupApi.tsRepository: TanStack/form
Length of output: 39
🏁 Script executed:
# Search the entire FieldGroupApi file for "update" method
rg "update\s*=" packages/form-core/src/FieldGroupApi.tsRepository: TanStack/form
Length of output: 39
🏁 Script executed:
# Get the line numbers and search for all methods systematically
grep -n "^\s*[a-zA-Z_][a-zA-Z0-9_]*\s*=" packages/form-core/src/FieldGroupApi.ts | grep -v "this\." | tail -40Repository: TanStack/form
Length of output: 1248
🏁 Script executed:
# Look at the complete FieldGroupApi file
wc -l packages/form-core/src/FieldGroupApi.tsRepository: TanStack/form
Length of output: 100
🏁 Script executed:
# Get the middle and end sections
sed -n '200,250p' packages/form-core/src/FieldGroupApi.tsRepository: TanStack/form
Length of output: 1260
🏁 Script executed:
# Check if there's an update method anywhere in FieldGroupApi
grep -n "update" packages/form-core/src/FieldGroupApi.tsRepository: TanStack/form
Length of output: 356
🏁 Script executed:
# Get the complete constructor implementation
sed -n '225,310p' packages/form-core/src/FieldGroupApi.tsRepository: TanStack/form
Length of output: 2237
🏁 Script executed:
# Verify the complete file structure - get the last section
tail -30 packages/form-core/src/FieldGroupApi.tsRepository: TanStack/form
Length of output: 796
Add update mechanism to track prop changes in useFieldGroup.
This hook initializes formLensApi once and never updates it. Unlike useField and useForm which call .update(opts) on every render to stay synchronized with prop changes, useFieldGroup has no update path. If opts.form, opts.fields, opts.defaultValues, opts.onSubmitMeta, or opts.formComponents change after the initial render, the lens and its wrapper components (AppField, Field, Subscribe) will route through stale references.
FieldGroupApi does not expose an update() method. Either add one (matching the FieldApi and FormApi pattern), or recreate the FieldGroupApi when its binding inputs change.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@packages/preact-form/src/useFieldGroup.tsx` around lines 169 - 254,
useFieldGroup currently creates formLensApi once and never syncs it with
changing inputs; add an update path so wrappers (extendedApi.AppField, Field,
Subscribe) don't hold stale refs. Fix by either adding an update(opts) method to
FieldGroupApi (matching FieldApi/FormApi) and calling formLensApi.update(opts)
on each render (or inside a useEffect) to propagate changes to opts.form,
opts.fields, opts.defaultValues, opts.onSubmitMeta, and opts.formComponents, or
recreate the FieldGroupApi instance when those specific binding inputs change
(replace the useState factory with a keyed memo/rehydration that re-instantiates
FieldGroupApi when opts.form/fields/defaultValues/onSubmitMeta/formComponents
change); ensure the same extendedApi properties
(AppField/AppForm/Field/Subscribe) continue to reference the updated internal
form instance.

🎯 Changes
@tanstack/preact-formpackage to the monorepo.@tanstack/preact-storeinstead of@tanstack/react-store.useIdfrompreact/hooksfor form ID generation.preactpeer dependency to>10.11.0for useId support.Summary by CodeRabbit
New Features
Documentation
Examples
Tests
Chores