T
TanStack2mo ago
rival-black

Typed errors for useFieldContext?

I'm following the guide on Form Composition on the official website. I really like the idea of being able to abstract some of these fields into their own components. But I'm trying to render the errors that can come from this field by extracting the field state using useStore. The issue is that useStore(field.store, (state) => state.meta.errors) is returning any[]. and checking the documentation a second time along with the types returned via createFormHookContexts it seems as though that's intentional. I found this a bit weird as my experience with you guys is that you're very strict about typing so I figured I must be doing something wrong. The result is that I have to do stuff like this:
import { useStore } from "@tanstack/react-form";
import { Field, FieldGroup, FieldLabel } from "@/components/ui/field";
import { Input } from "@/components/ui/input";
import { useFieldContext } from "./hooks/form-context";

export default function TextField({ label }: { label: string }) {
const field = useFieldContext<string>();

const errors = useStore(field.store, (state) => state.meta.errors);

return (
<FieldGroup>
<Field>
<FieldLabel>{label}</FieldLabel>

<Input
name={field.name}
value={field.state.value}
onChange={(e) => field.handleChange(e.target.value)}
onBlur={field.handleBlur}
/>

{errors.map((error: { message?: string }, index: number) => (
// biome-ignore lint/suspicious/noArrayIndexKey: we need to use the index as the key
<div key={index} style={{ color: "red" }}>
{typeof error === "string"
? error
: error.message || "Validation error"}
</div>
))}
</Field>
</FieldGroup>
);
}
import { useStore } from "@tanstack/react-form";
import { Field, FieldGroup, FieldLabel } from "@/components/ui/field";
import { Input } from "@/components/ui/input";
import { useFieldContext } from "./hooks/form-context";

export default function TextField({ label }: { label: string }) {
const field = useFieldContext<string>();

const errors = useStore(field.store, (state) => state.meta.errors);

return (
<FieldGroup>
<Field>
<FieldLabel>{label}</FieldLabel>

<Input
name={field.name}
value={field.state.value}
onChange={(e) => field.handleChange(e.target.value)}
onBlur={field.handleBlur}
/>

{errors.map((error: { message?: string }, index: number) => (
// biome-ignore lint/suspicious/noArrayIndexKey: we need to use the index as the key
<div key={index} style={{ color: "red" }}>
{typeof error === "string"
? error
: error.message || "Validation error"}
</div>
))}
</Field>
</FieldGroup>
);
}
It definitely works but I feel like maybe this should be a field group. That also doesn't feel right though so I'm not sure what to do with this. Any ideas?
3 Replies
rival-black
rival-black2mo ago
The field context doesn't know about the incoming errors, as they could come from anywhere. The errors are typed in the callback of AppField or Field, so if you want to have them pretyped, pass them through props instead
<form.Field name="foo">
{field => <FieldErrors errors={field.state.meta.errors} /> }
</form.Field>
<form.Field name="foo">
{field => <FieldErrors errors={field.state.meta.errors} /> }
</form.Field>
rival-black
rival-blackOP2mo ago
OK fair enough. I fiured it was something like tha. Thanks!
rival-black
rival-black2mo ago
let me know if other issues come up

Did you find this page helpful?