# Controlled fields

By default, all form fields are uncontrolled because that's the default behavior of the browser. For a simple login or contact form this is quite sufficient.

## Why controlled?

As soon as your forms become more complex, for example you set initial values or change the values of a form field via <Link href="/methods/api/setInput/">`setInput`</Link>, it becomes necessary that you control your fields yourself. For example, depending on which HTML form field you use, you may need to set the `value`, `checked` or `selected` attributes.

### Text input example

For a text input field you simply add the `value` attribute and pass the value of the field or in case of `undefined` an empty string:

```tsx
<Field of={loginForm} path={['firstName']}>
  {(field) => (
    <input
      {...field.props}
      type="text"
      // Pass value or empty string
      value={field.input ?? ''}
    />
  )}
</Field>
```

### Exception for files

The HTML `<input type="file" />` element is an exception because it cannot be controlled. However, you have the possibility to control the UI around it. For inspiration you can use the code of our <a href={`${import.meta.env.PUBLIC_GITHUB_URL}/blob/main/playgrounds/solid/src/components/FileInput.tsx`} target="\_blank" rel="noreferrer">`FileInput`</a> component from our <Link href="/playground/">playground</Link>.

## Numbers and dates

To make the fields of numbers and dates controlled, further steps are required, because the `<input />` element natively understands only strings as value.

### Number input example

Since not every input into an `<input type="number" />` field is a valid number, for example when typing floating numbers, the value may be `NaN` in between. You have to catch this case, otherwise the whole input will be removed when `NaN` is passed. It is best to encapsulate this logic in a separate component as described in the <Link href="/solid/guides/input-components/">input components</Link> guide.

```tsx
import type { FieldElementProps } from '@formisch/solid';
import { createMemo, splitProps } from 'solid-js';

interface NumberInputProps extends FieldElementProps {
  type: 'number';
  label?: string;
  placeholder?: string;
  input: number | undefined;
  errors: [string, ...string[]] | null;
  required?: boolean;
}

export function NumberInput(props: NumberInputProps) {
  const [, inputProps] = splitProps(props, ['input', 'label', 'errors']);

  // Update memo if value is not `NaN`
  const getValue = createMemo<number | undefined>(
    (prevValue) => (!Number.isNaN(props.input) ? props.input : prevValue),
    undefined
  );

  return (
    <div>
      {props.label && <label for={props.name}>{props.label}</label>}
      <input
        {...inputProps}
        id={props.name}
        value={getValue() ?? ''}
        aria-invalid={!!props.errors}
        aria-errormessage={`${props.name}-error`}
      />
      {props.errors && <div id={`${props.name}-error`}>{props.errors[0]}</div>}
    </div>
  );
}
```

### Date input example

A date or a number representing a date must be converted to a string before it can be passed to an `<input type="date" />` field. Since it is a calculated value, you can use `createMemo` for this.

```tsx
import type { FieldElementProps } from '@formisch/solid';
import { createMemo, splitProps } from 'solid-js';

interface DateInputProps extends FieldElementProps {
  type: 'date';
  label?: string;
  placeholder?: string;
  input: Date | number | undefined;
  errors: [string, ...string[]] | null;
  required?: boolean;
}

export function DateInput(props: DateInputProps) {
  const [, inputProps] = splitProps(props, ['input', 'label', 'errors']);

  // Transform date or number to string
  const getValue = createMemo(() =>
    props.input &&
    !Number.isNaN(
      typeof props.input === 'number' ? props.input : props.input.getTime()
    )
      ? new Date(props.input).toISOString().split('T', 1)[0]
      : ''
  );

  return (
    <div>
      {props.label && <label for={props.name}>{props.name}</label>}
      <input
        {...inputProps}
        id={props.name}
        value={getValue()}
        aria-invalid={!!props.errors}
        aria-errormessage={`${props.name}-error`}
      />
      {props.errors && <div id={`${props.name}-error`}>{props.errors[0]}</div>}
    </div>
  );
}
```

## Custom inputs and component libraries

Some component libraries don't expose the underlying native HTML element, which means you cannot spread `field.props` onto them. For these cases, use `field.onInput` to set the value programmatically.

```tsx
import { DatePicker } from 'some-component-library';

<Field of={form} path={['date']}>
  {(field) => (
    <DatePicker
      value={field.input}
      onChange={(newDate) => field.onInput(newDate)}
    />
  )}
</Field>;
```

This is useful for:

- **Component libraries** that wrap native elements without exposing them
- **Complex custom inputs** like date pickers, rich text editors, or color pickers

The `field.onInput` method updates the field value and triggers validation, just like a native input would.

## Next steps

Now that you understand controlled fields, you can explore more advanced topics like <Link href="/solid/guides/nested-fields/">nested fields</Link> and <Link href="/solid/guides/field-arrays/">field arrays</Link> to handle complex form structures.
