TanStack
Auto
TanStack Startbeta
示例文档GitHub
TanStack Routernew
示例文档GitHub
TanStack Query
示例文档GitHub
TanStack Table
示例文档Github
TanStack Formnew
示例文档Github
TanStack Virtual
示例文档Github
TanStack Ranger
示例文档Github
TanStack Store
示例文档Github
TanStack Config
文档Github
支持
学习
Discord
周边
博客
GitHub
Ethos
TanStack
Auto
TanStack Startbeta
示例文档GitHub
TanStack Routernew
示例文档GitHub
TanStack Query
示例文档GitHub
TanStack Table
示例文档Github
TanStack Formnew
示例文档Github
TanStack Virtual
示例文档Github
TanStack Ranger
示例文档Github
TanStack Store
示例文档Github
TanStack Config
文档Github
支持
学习
Discord
周边
博客
GitHub
Ethos
返回

TanStack Form v1 发布

作者:Corbin Crutchley,发布于 2025 年 3 月 3 日。

TanStack Form v1

我们很高兴地宣布 TanStack Form 的首个稳定版本已发布,可以用于生产环境了!🥳

我们在发布时支持五个框架:React、Vue、Angular、Solid 和 Lit,以及每个特定框架的众多功能。

如何安装

shell
$ npm i @tanstack/react-form
# or
$ npm i @tanstack/vue-form
# or
$ npm i @tanstack/angular-form
# or
$ npm i @tanstack/solid-form
# or
$ npm i @tanstack/lit-form
$ npm i @tanstack/react-form
# or
$ npm i @tanstack/vue-form
# or
$ npm i @tanstack/angular-form
# or
$ npm i @tanstack/solid-form
# or
$ npm i @tanstack/lit-form

一点历史

大约两年前,我看到了 Tanner 在 BlueSky(当时还是一个仅限邀请的平台)上发布的帖子,宣布他正在开发一个新项目:TanStack Form。

A back and forth between Tanner and myself on Bluesky about TanStack Form

当时,我刚刚发布了一个名为“HouseForm”的 React 替代表单库,并且立即被 Tanner 的库带来的一些想法所吸引。

我很幸运地参加了一个 Tanner 之后也要参加的黑客马拉松,我们得以抽出一些时间来研究将 HouseForm 中的一些 API 集成到该项目中。

从那时起,Tanner 将 Form 的大部分管理权移交给了我和一个很棒的额外维护者团队。

那么,在那段时间里我们构建了什么呢?

功能

长期酝酿的好处之一是,TanStack Form 发布时就带有一系列您可以立即利用的功能。

让我们以 React 的适配器为例,介绍其中的一小部分功能。

极致的类型安全

像许多 TanStack 项目一样,Form 彻底改变了“类型安全”表单库的含义。

tsx
const form = useForm({
    defaultValues: {
        name: "",
        age: 0
    }
});

// TypeScript will correctly tell you that `firstName` is not a valid field
<form.Field name="firstName"/>

// TypeScript will correctly tell you that `name`'s type is a `string`, not a `number`
<form.Field name="name" children={field => <NumberInput value={field.state.value}/>}/>
const form = useForm({
    defaultValues: {
        name: "",
        age: 0
    }
});

// TypeScript will correctly tell you that `firstName` is not a valid field
<form.Field name="firstName"/>

// TypeScript will correctly tell you that `name`'s type is a `string`, not a `number`
<form.Field name="name" children={field => <NumberInput value={field.state.value}/>}/>

我们甚至支持类型检查在 <form.Field> 中返回的错误

tsx
<form.Field
  name="age"
  validators={{
    onChange: ({ value }) => (value < 12 ? { tooYoung: true } : undefined),
  }}
  children={(field) => (
    <>
      <NumberInput value={field.state.value} />
      // TypeScript will correctly tell you that `errorMap.onChange` // is an object,
      not a string
      <p>{field.state.meta.errorMap.onChange}</p>
    </>
  )}
/>
<form.Field
  name="age"
  validators={{
    onChange: ({ value }) => (value < 12 ? { tooYoung: true } : undefined),
  }}
  children={(field) => (
    <>
      <NumberInput value={field.state.value} />
      // TypeScript will correctly tell you that `errorMap.onChange` // is an object,
      not a string
      <p>{field.state.meta.errorMap.onChange}</p>
    </>
  )}
/>

哦,对了,我们还支持基于字段的验证以及表单验证。混合搭配使用它们吧!

最好的部分是什么?您无需传递任何 typescript 泛型即可获得这种级别的类型安全。一切都从您的运行时使用情况中推断出来。

Schema 验证

感谢 Zod、Valibot 和 ArkType 的创建者们的出色工作,我们开箱即用地支持 Standard Schema;无需其他软件包。

tsx
const userSchema = z.object({
  age: z.number().gte(13, 'You must be 13 to make an account'),
})

function App() {
  const form = useForm({
    defaultValues: {
      age: 0,
    },
    validators: {
      onChange: userSchema,
    },
  })
  return (
    <div>
      <form.Field
        name="age"
        children={(field) => {
          return <>{/* ... */}</>
        }}
      />
    </div>
  )
}
const userSchema = z.object({
  age: z.number().gte(13, 'You must be 13 to make an account'),
})

function App() {
  const form = useForm({
    defaultValues: {
      age: 0,
    },
    validators: {
      onChange: userSchema,
    },
  })
  return (
    <div>
      <form.Field
        name="age"
        children={(field) => {
          return <>{/* ... */}</>
        }}
      />
    </div>
  )
}

异步验证

但这还不是全部!我们还支持使用异步函数来验证您的代码;完全内置了防抖和基于 AbortSignal 的取消功能

tsx
<form.Field
  name="age"
  asyncDebounceMs={500}
  validators={{
    onBlurAsync: async ({ value, signal }) => {
      const currentAge = await fetchCurrentAgeOnProfile({ signal })
      return value < currentAge ? 'You can only increase the age' : undefined
    },
  }}
/>
<form.Field
  name="age"
  asyncDebounceMs={500}
  validators={{
    onBlurAsync: async ({ value, signal }) => {
      const currentAge = await fetchCurrentAgeOnProfile({ signal })
      return value < currentAge ? 'You can only increase the age' : undefined
    },
  }}
/>

平台支持

不仅像我们一开始提到的那样支持多个框架;我们还支持多个运行时。无论您使用的是 React Native、NativeScript,甚至是像 Next.js 或 TanStack Start 这样的 SSR 解决方案,我们都能满足您的需求。

事实上,如果您使用的是 SSR 解决方案,我们甚至可以使服务器端表单验证变得轻而易举

typescript
// app/routes/index.tsx, but can be extracted to any other path
import { createServerValidate, getFormData } from '@tanstack/react-form/start'
import { yourSchemaHere } from '~/constants/forms'

const serverValidate = createServerValidate({
  ...formOpts,
  onServerValidate: yourSchemaHere,
})

export const getFormDataFromServer = createServerFn({ method: 'GET' }).handler(
  async () => {
    return getFormData()
  }
)
// app/routes/index.tsx, but can be extracted to any other path
import { createServerValidate, getFormData } from '@tanstack/react-form/start'
import { yourSchemaHere } from '~/constants/forms'

const serverValidate = createServerValidate({
  ...formOpts,
  onServerValidate: yourSchemaHere,
})

export const getFormDataFromServer = createServerFn({ method: 'GET' }).handler(
  async () => {
    return getFormData()
  }
)

此代码示例排除了一些相关代码,以保持简洁。有关我们 SSR 集成的更多详细信息,请查看我们的文档。

瞧!完全相同的验证逻辑在您的前端和后端都在运行。即使在用户的浏览器上禁用 JavaScript,您的表单也会显示错误!

展望未来

然而,我们并没有止步于此——既然我们已经稳定了,我们计划为 v1 添加新功能。这些功能包括

  • 持久化 API
  • Svelte 5 适配器
  • 更好的 DX,用于在提交时转换值
  • 表单组

以及更多。

感谢您

感谢的人太多了,如果一一感谢的话,将没完没了。因此,我将向我想感谢的各个群体致谢。

  • 感谢我们的贡献者们: 很多人齐心协力才促成了这一切。 从其他 TanStack 项目维护者的指导,到随手的 PR,都帮助我们达成了目标。

  • 感谢我们的早期采用者: 感谢那些信任我们,并为我们的 API 和功能提供了宝贵反馈的人们。

  • 感谢报道我们工具的内容创作者们: 你们为我们的项目带来了更多关注,并通过教育和反馈使其变得更好。

  • 感谢更广泛的社区: 你们对使用我们工具的热情极大地鼓舞了团队。

最后,感谢您花时间阅读和探索我们的最新工具。 ❤️

在 GitHub 上编辑