Add fields to form

To add a field to your form, you use the Field component. It is headless and makes its state accessible to its children via a render prop.

Field component

The Field component has a mandatory property called name which you use to specify which field it is. If you use TypeScript, you don't have to type the name yourself thanks to autocompletion.

Children prop

As a child, you pass a function to the Field component that returns JSX. You can use the first parameter of the function to access the current state of the field and the second parameter you have to pass to an HTML <input />, <textarea /> or <select /> element to connect it to your form.

export default component$(() => {
  const [loginForm, { Form, Field }] = useForm<LoginForm>({
    loader: useFormLoader(),
  });

  return (
    <Form>
      <Field name="email">
        {(field, props) => <input {...props} type="email" />}
      </Field>
      <Field name="password">
        {(field, props) => <input {...props} type="password" />}
      </Field>
      <button type="submit">Login</button>
    </Form>
  );
});

Please let us know via email or the issues on GitHub if something is unclear or you have ideas on how we can further improve the API and documentation.

Headless design

The Field 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 HTML elements, custom components or an external UI library.

Data types

If you want your field to capture a data type other than string, a second property called type is required. However, based on your type definition, we will recognize this, point it out in your code and help you fill in the value using autocompletion. This way we know your data type at runtime and can ensure that only valid values are captured.

<Field name="age" type="number">
  {(field, props) => <input {...props} type="number" />}
</Field>

Type safety

The API design of the Field component results in a fully type-safe form. For example, if you change the name of a field, TypeScript will immediately alert you. Also, you can't mistype when adding an error message, because it is taken from the provided state of the field. More about this on the next page.

Field state

In addition to the current value and the error message of a field, the Field component also tracks whether a field has been touched, is dirty or active.

Touched and dirty

A field is touched whenever the blur event has been triggered at least once or an input has been made and is dirty if the current value does not correspond to the initial value.

Active state

A field is active when it is part of the DOM. The active state is important because by default Modular Forms only takes active fields into account, e.g. when validating or returning the current form values. If you don't want this behavior, you can use the keepActive property of the Field component to prevent a field from becoming inactive or the shouldActive property or option of the Form component or the various methods to ignore the active state entirely.

Why is there an active state?

The active state allows that when fields are shown or hidden depending on the value of another field, that only the values of the shown fields are validated and returned. The payment form in our playground shows this behavior.