Skip to content
IC Reactor

TanStack Form Demo

Learn how to generate a TanStack Form from a Candid method: FieldVisitor turns the method’s argument types into field metadata, default values and a Zod schema, and one component renders every field from that metadata. The demo builds the form for an updateProfile(UserProfile) IDL and shows the submitted values; it does not call a canister.

  • Generated Fields: One field per Candid type, chosen by field.component (records, variants, optionals, vectors, text, numbers, booleans).
  • Defaults: meta.defaults seeds the form.
  • Validation: meta.schema validates every change.
src/App.tsx
import { FieldVisitor } from "@ic-reactor/candid"
import { IDL } from "@icp-sdk/core/candid"
import { useForm } from "@tanstack/react-form"
const UserProfile = IDL.Record({
username: IDL.Text,
age: IDL.Nat,
email: IDL.Text,
bio: IDL.Opt(IDL.Text),
tags: IDL.Vec(IDL.Text),
})
const visitor = new FieldVisitor()
const meta = visitor.visitFunc(IDL.Func([UserProfile], [], []), "updateProfile")
function CandidForm() {
const form = useForm({
defaultValues: meta.defaults,
validators: { onChange: meta.schema },
onSubmit: async ({ value }) => {
console.log("Form Submitted:", value)
},
})
return (
<form
onSubmit={(e) => {
e.preventDefault()
form.handleSubmit()
}}
>
{meta.args.map((field, index) => (
<DynamicField key={field.name || index} field={field} form={form} />
))}
<button type="submit">Submit</button>
</form>
)
}

DynamicField is the demo’s own component (in src/App.tsx): it renders one FieldNode with the input its component names.

To send the values to a canister, convert them from display types to Candid first; see CandidFormVisitor.