Skip to main content

How to use in input

There are three ways to declare a controlled input, two using the form system and the other in the traditional way, with useState.

It's important to declare form and fields outside the lifecycle of any component. Declaring them inside a component may cause several issues.

Declaring through a form field

If you have a form with a field, then you should declare your input like this:

./src/example.tsx
import { form, fields } from '@codeleap/form'

const myForm = form('example', {
field1: fields.text({ label: 'Field 1' }),
})

function Form() {
return <TextInput {...myForm.register('field1')} />
}

This is the default path for package-aware inputs in normal form screens.

Declaring through a field

If you don't have a form but have an input and want to take advantage of the full validation system and global value access, then declare your own field and pass it to the input.

All inputs accept this field property.

./src/example.tsx
import { fields } from '@codeleap/form'

const fieldExample = fields.text({
label: 'Example',
placeholder: 'Placeholder',
onValueChange: (value) => console.log(value),
})

function Field() {
return <TextInput field={fieldExample} />
}

This is the usual choice for one reusable validated control without a full form object.

Declaring through a custom input

If you are building your own input component, the component should usually accept a specific field type and consume it through useField(...).

./src/TextInput.tsx
import { type TextField, fields, useField } from '@codeleap/form'

type Props = {
field?: TextField<any>
placeholder?: string
}

function TextInput({ field: fieldProp, placeholder }: Props) {
const { value, setValue, validation, options } = useField(fieldProp, [undefined, []], fields.text)

return (
<input
value={value ?? ''}
onChange={(e) => setValue(e.target.value)}
onBlur={validation.onInputBlurred}
placeholder={placeholder ?? options.placeholder}
/>
)
}

Do not flatten register(...) into guessed controlled props before the custom component even receives the field.

Declaring through a useState

If you don't have a form and don't need to use the benefits of a field, then declare using the standard approach.

All inputs receive onValueChange, and this property should be used.

./src/example.tsx
import { useState } from 'react'

function Component() {
const [value, setValue] = useState('')

return <TextInput value={value} onValueChange={setValue} />
}