# Add form fields

> This document is the Markdown version of [formisch.dev/react-native/guides/add-form-fields/](https://formisch.dev/react-native/guides/add-form-fields/). For the complete documentation index, see [llms.txt](https://formisch.dev/llms.txt).

To add a field to your form, you can use the [`Field`](/react-native/api/Field.md) component or the [`useField`](/react-native/api/useField.md) hook. Both are headless and provide access to field state for building your form UI.

## Field component

The [`Field`](/react-native/api/Field.md) component has two mandatory properties: `of` which accepts the form store, and `path` which specifies which field to connect. If you use TypeScript, you get full autocompletion for the path based on your schema.

### Children prop

As a child, you pass a function to the [`Field`](/react-native/api/Field.md) component that returns JSX. The function receives the field store as its parameter, which includes the current value, error messages, and props to spread onto your `TextInput` component.

```tsx
import { Field, handleSubmit, useForm } from '@formisch/react-native';
import { Button, TextInput, View } from 'react-native';
import * as v from 'valibot';

const LoginSchema = v.object({
  email: v.pipe(v.string(), v.email()),
  password: v.pipe(v.string(), v.minLength(8)),
});

export default function LoginScreen() {
  const loginForm = useForm({
    schema: LoginSchema,
  });

  const submitForm = handleSubmit(loginForm, (output) => console.log(output));

  return (
    <View>
      <Field of={loginForm} path={['email']}>
        {(field) => (
          <TextInput
            {...field.props}
            value={field.input}
            autoCapitalize="none"
            keyboardType="email-address"
          />
        )}
      </Field>
      <Field of={loginForm} path={['password']}>
        {(field) => (
          <TextInput {...field.props} value={field.input} secureTextEntry />
        )}
      </Field>
      <Button title="Login" onPress={submitForm} />
    </View>
  );
}
```

> **Important:** Text inputs are controlled. Always pass `value={field.input}` so that initial values from `initialInput` and programmatic updates via methods like [`setInput`](/methods/api/setInput.md) or [`reset`](/methods/api/reset.md) are reflected in the UI.

> **Important:** The `onChangeText` handler in `field.props` receives the value as a string. Fields with a non-string schema like `v.number()` or `v.date()` therefore need to use `field.onChange` to store the value with the correct type. See the [controlled fields](/react-native/guides/controlled-fields.md) guide to learn more.

### Headless design

The [`Field`](/react-native/api/Field.md) component does not render its own UI elements. It is headless and provides only the data layer of the field. This allows you to freely define your user interface. You can use React Native's built-in components, custom components or an external UI library.

### Path array

The `path` property accepts an array of strings and numbers that represents the path to the field in your schema. For top-level fields, it's simply the field name wrapped in an array:

```tsx
<Field of={loginForm} path={['email']}>
  {(field) => <TextInput {...field.props} value={field.input} />}
</Field>
```

For nested fields, the path reflects the structure of your schema:

```tsx
// For a schema like: v.object({ user: v.object({ email: v.string() }) })
<Field of={form} path={['user', 'email']}>
  {(field) => <TextInput {...field.props} value={field.input} />}
</Field>
```

### Type safety

The API design of the [`Field`](/react-native/api/Field.md) component results in a fully type-safe form. For example, if you change your schema, TypeScript will immediately alert you if the path is invalid. The field state is also fully typed based on your schema, giving you autocompletion for properties like `field.input`.

## useField hook

For very complex forms where you create individual components for each form field, Formisch provides the [`useField`](/react-native/api/useField.md) hook. It allows you to access the field state directly within your component logic.

```tsx
import { useField } from '@formisch/react-native';
import type { FormStore } from '@formisch/react-native';
import { Text, TextInput, View } from 'react-native';
import * as v from 'valibot';

type EmailInputProps = {
  form: FormStore<v.GenericSchema<{ email: string }>>;
};

function EmailInput(props: EmailInputProps) {
  const field = useField(props.form, { path: ['email'] });

  return (
    <View>
      <TextInput
        {...field.props}
        value={field.input}
        autoCapitalize="none"
        keyboardType="email-address"
      />
      {field.errors && <Text>{field.errors[0]}</Text>}
    </View>
  );
}
```

### When to use which

- **Use [`Field`](/react-native/api/Field.md) component**: When defining multiple fields in the same component. It ensures you don't accidentally access the wrong field store.
- **Use [`useField`](/react-native/api/useField.md) hook**: When creating field components for single fields. It allows you to access field state in your component logic.

The [`Field`](/react-native/api/Field.md) component is essentially a thin wrapper around [`useField`](/react-native/api/useField.md) that allows you to access the field state within JSX code.

## Field store

The field store provides access to the following properties. They are plain values, but because Formisch wires into React's rendering, your components re-render when they change.

- `props`: Props to spread onto your `TextInput` component (includes the event handlers and the ref callback to register the field).
- `input`: The current input value of the field.
- `errors`: The current array of error messages if validation fails.
- `isTouched`: Whether the field has been touched.
- `isEdited`: Whether the field's value has been changed.
- `isDirty`: Whether the current input differs from the initial input.
- `isValid`: Whether the field passes all validation rules.
- `onChange`: Sets the field input value programmatically. Use this for components that don't render a text input (e.g., `Switch`, sliders or picker components).

## Next steps

Now that you know how to add fields to your form, continue to the [input components](/react-native/guides/input-components.md) guide to learn about creating reusable input components for your forms.
