Input Field

Text input component integrated with TanStack Form for type-safe form management. Supports tooltips and InputGroup integration for addons and buttons.

npx shadcn@latest add https://shuip.plvo.dev/r/tsf-input-field.json

Preview

Loading...

InputField is a text input component that encapsulates TanStack Form's field management with shadcn/ui's design system. It handles all the boilerplate of connecting form state to an input element: wiring event handlers, displaying errors, managing touched states, and rendering consistent UI.

This component is useful when you want to quickly add form inputs without manually setting up form.Field render props, input bindings, and error display logic for every field.

Built-in features

  • Type-safe field names: Uses DeepKeys<TFormData> for autocomplete and compile-time validation
  • Nested path support: Access deeply nested values like user.profile.email
  • Tooltip integration: Optional InfoIcon button with tooltip content via tooltip prop
  • InputGroup ready: Supports shadcn InputGroup for seamless addon integration
  • Async validation: Built-in debouncing and loading states for API validation
  • Full type inference: Field value types automatically inferred from form schema

Less boilerplate

TanStack Form's standard approach uses render props to access field state:

<form.Field
name="email"
validators={{
onChange: ({ value }) => !value.includes('@') ? 'Invalid email' : undefined
}}
>
{(field) => (
<>
<label htmlFor={field.name}>Email</label>
<input
id={field.name}
name={field.name}
type="email"
value={field.state.value}
onChange={(e) => field.handleChange(e.target.value)}
onBlur={field.handleBlur}
/>
{!field.state.meta.isValid && (
<span>{field.state.meta.errors.join(', ')}</span>
)}
</>
)}
</form.Field>

With InputField, this reduces to a single declarative component:

<InputField
form={form}
name='email'
label='Email'
props={{ type: 'email' }}
formProps={{
validators: {
onChange: ({ value }) => !value.includes('@') ? 'Invalid email' : undefined
}
}}
/>

Common use cases

const form = useForm({
defaultValues: {
name: '',
email: '',
age: 0,
},
onSubmit: async ({ value }) => {
await saveData(value)
},
})
// Basic text input
<InputField
form={form}
name='name'
label='Name'
description='Your full name'
/>
// Email with validation
<InputField
form={form}
name='email'
label='Email'
props={{ type: 'email' }}
formProps={{
validators: {
onChange: ({ value }) =>
!value.includes('@') ? 'Invalid email' : undefined
}
}}
/>
// With tooltip
<InputField
form={form}
name='username'
label='Username'
tooltip='Username must be unique and 3-20 characters'
formProps={{
validators: {
onChange: ({ value }) =>
value.length < 3 ? 'Too short' : undefined
}
}}
/>
// Number input
<InputField
form={form}
name='age'
label='Age'
props={{ type: 'number', min: 0, max: 120 }}
/>

Examples

Async Validation

Loading...

Default

Loading...

Nested Path

Loading...

Tooltip

Loading...

Props

NameTypeDescription
formReactFormApi<TFormData>Form instance from useForm hook
nameDeepKeys<TFormData>Type-safe field name (supports nested paths like "user.email")
labelstring?Field label text
descriptionstring?Helper text displayed below the input
tooltipReact.ReactNode?Tooltip content displayed in an info icon button
formPropsPartial<FieldOptions>?TanStack Form field options (validators, etc.)
fieldPropsReact.ComponentProps<typeof Field>?Props passed to the Field wrapper component
propsReact.ComponentProps<'input'>?Native HTML input props (type, placeholder, disabled, etc.)