Skip to content
Open
Show file tree
Hide file tree
Changes from 12 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
99 changes: 99 additions & 0 deletions docs/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,15 @@
}
]
},
{
"label": "preact",
"children": [
{
"label": "Quick Start",
"to": "framework/preact/quick-start"
}
]
},
{
"label": "vue",
"children": [
Expand Down Expand Up @@ -169,6 +178,67 @@
}
]
},
{
"label": "preact",
"children": [
{
"label": "Basic Concepts",
"to": "framework/preact/guides/basic-concepts"
},
{
"label": "Form Validation",
"to": "framework/preact/guides/validation"
},
{
"label": "Dynamic Validation",
"to": "framework/preact/guides/dynamic-validation"
},
{
"label": "Async Initial Values",
"to": "framework/preact/guides/async-initial-values"
},
{
"label": "Arrays",
"to": "framework/preact/guides/arrays"
},
{
"label": "Linked Fields",
"to": "framework/preact/guides/linked-fields"
},
{
"label": "Reactivity",
"to": "framework/preact/guides/reactivity"
},
{
"label": "Listeners",
"to": "framework/preact/guides/listeners"
},
{
"label": "Custom Errors",
"to": "framework/preact/guides/custom-errors"
},
{
"label": "Submission Handling",
"to": "framework/preact/guides/submission-handling"
},
{
"label": "UI Libraries",
"to": "framework/preact/guides/ui-libraries"
},
{
"label": "Focus Management",
"to": "framework/preact/guides/focus-management"
},
{
"label": "Form Composition",
"to": "framework/preact/guides/form-composition"
},
{
"label": "Debugging",
"to": "framework/preact/guides/debugging"
}
]
},
{
"label": "vue",
"children": [
Expand Down Expand Up @@ -432,6 +502,35 @@
}
]
},
{
"label": "preact",
"children": [
{
"label": "Preact Reference",
"to": "framework/preact/reference/index"
},
{
"label": "Variables / Field",
"to": "framework/preact/reference/variables/Field"
},
{
"label": "Functions / useField",
"to": "framework/preact/reference/functions/useField"
},
{
"label": "Functions / useForm",
"to": "framework/preact/reference/functions/useForm"
},
{
"label": "Types / FieldComponent",
"to": "framework/preact/reference/type-aliases/FieldComponent"
},
{
"label": "Types / UseField",
"to": "framework/preact/reference/type-aliases/UseField"
}
]
},
{
"label": "vue",
"children": [
Expand Down
124 changes: 124 additions & 0 deletions docs/framework/preact/guides/arrays.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
---
id: arrays
title: Arrays
---

TanStack Form supports arrays as values in a form, including sub-object values inside of an array.

## Basic Usage

To use an array, you can use `field.state.value` on an array value:

```jsx
function App() {
const form = useForm({
defaultValues: {
people: [],
},
})

return (
<form.Field name="people" mode="array">
{(field) => (
<div>
{field.state.value.map((_, i) => {
// ...
})}
</div>
)}
</form.Field>
)
}
```

This will generate the mapped JSX every time you run `pushValue` on `field`:

```jsx
<button onClick={() => field.pushValue({ name: '', age: 0 })} type="button">
Add person
</button>
```

Finally, you can use a subfield like so:

```jsx
<form.Field key={i} name={`people[${i}].name`}>
{(subField) => (
<input
value={subField.state.value}
onInput={(e) => subField.handleChange(e.target.value)}
/>
)}
</form.Field>
```

## Full Example

```jsx
function App() {
const form = useForm({
defaultValues: {
people: [],
},
onSubmit({ value }) {
alert(JSON.stringify(value))
},
})

return (
<div>
<form
onSubmit={(e) => {
e.preventDefault()
e.stopPropagation()
form.handleSubmit()
}}
>
<form.Field name="people" mode="array">
{(field) => {
return (
<div>
{field.state.value.map((_, i) => {
return (
<form.Field key={i} name={`people[${i}].name`}>
{(subField) => {
return (
<div>
<label>
<div>Name for person {i}</div>
<input
value={subField.state.value}
onInput={(e) =>
subField.handleChange(e.target.value)
}
/>
</label>
</div>
)
}}
</form.Field>
)
})}
<button
onClick={() => field.pushValue({ name: '', age: 0 })}
type="button"
>
Add person
</button>
</div>
)
}}
</form.Field>
<form.Subscribe
selector={(state) => [state.canSubmit, state.isSubmitting]}
children={([canSubmit, isSubmitting]) => (
<button type="submit" disabled={!canSubmit}>
{isSubmitting ? '...' : 'Submit'}
</button>
)}
/>
</form>
</div>
)
}
```
51 changes: 51 additions & 0 deletions docs/framework/preact/guides/async-initial-values.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
---
id: async-initial-values
title: Async Initial Values
---

Let's say that you want to fetch some data from an API and use it as the initial value of a form.

While this problem sounds simple on the surface, there are hidden complexities you might not have thought of thus far.

For example, you might want to show a loading spinner while the data is being fetched, or you might want to handle errors gracefully.
Likewise, you could also find yourself looking for a way to cache the data so that you don't have to fetch it every time the form is rendered.

While we could implement many of these features from scratch, it would end up looking a lot like another project we maintain: [TanStack Query](https://tanstack.com/query).

As such, this guide shows you how you can mix-n-match TanStack Form with TanStack Query to achieve the desired behavior.

## Basic Usage

```tsx
import { useForm } from '@tanstack/preact-form'
import { useQuery } from '@tanstack/react-query'

Comment on lines +20 to +22
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 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/preact

Repository: 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 -30

Repository: 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 -10

Repository: 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 -15

Repository: 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:


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.

Suggested change
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).

export default function App() {
const {data, isLoading} = useQuery({
queryKey: ['data'],
queryFn: async () => {
await new Promise((resolve) => setTimeout(resolve, 1000))
return {firstName: 'FirstName', lastName: "LastName"}
}
})

const form = useForm({
defaultValues: {
firstName: data?.firstName ?? '',
lastName: data?.lastName ?? '',
},
onSubmit: async ({ value }) => {
// Do something with form data
console.log(value)
},
})

if (isLoading) return <p>Loading...</p>

return (
// ...
)
}
```

This will show a loading spinner until the data is fetched, and then it will render the form with the fetched data as the initial values.
Loading
Loading