Working with different input types
Since RVF validates and submits data directly from the html form
element by default,
nearly anything you can do with plain html forms can be done with RVF.
All native input types are supported out of the box.
When you're using RVF to observe or set the value of a field, it should hopefully "just work" the way you expect. But for completeness, this page is going to cover all the different input types and how they interact with RVF.
Common traits
Types
RVF exports helper types for the values of a few different input types.
For they most part, they're what you expect, but it's worth noting that empty inputs
are generally represented as null
in caes where the main type of the input isn't a string.
Setting default values
All input types (except for file
) can have their default value set using a string
.
For input types that represent some other type (like number
),
you can usually set the default value using that type.
Observing / setting values
The type of the value you get when you call form.value(fieldName)
is always
the same as the type you pass into defaultValues
.
You should use the same type when calling form.setValue(fieldName, value)
.
Validating
Unless you're using state mode, the data received by your validator
will always be a string
(except for file
inputs) OR a string[]
.
- If only one input is in the form for a given field, the value will be a
string
. - If multiple inputs in the form have the same name, the value will be a
string[]
.
If you are using state mode, then the value passed to your validator will be the same value
that you would get out of form.value("myField")
.
Number inputs
Relevant types
NumberInputValue
number | null
Setting default values
Number inputs can be set using either a number
, a string
, or null
.
const form = useForm({
defaultValues: {
age: "25",
numberOfPeople: 10,
},
})
const age = form.value("age");
// ^? string
const numberOfPeople = form.value("numberOfPeople");
// ^? number
Observing / setting values
- An empty number input will always have a value of
null
. - If you set the default value using a
string
, the value will always be astring
(ornull
). - If you set the default value using a
number
ornull
, the value will always be anumber
(ornull
).
Validating
Like most input types, the value inside the native form
element is always a string.
Unless you're using state mode, your validator should be able to handle that.
- If you're using
zod
, you can usez.coerce.number
to handle this. - If you're using
yup
, thenyup.number()
already handles this.
If you're using state mode, then the value passed to your validator will be the same value
that you would get out of form.value("myField")
.
Checkboxes & Checkbox groups
Checkboxes are pretty versatile. They can be used as a simple boolean flag, or as a group of checkboxes represented by an array of strings. And this is all without any extra work or controlled components.
Usage
Call getInputProps
for each checkbox input and pass in a type of "checkbox"
and the value
of the checkbox.
<label>
Checkbox with a value
<input
{...form.getInputProps("myCheckbox1", {
type: "checkbox",
value: "checkbox-value"
})}
/>
</label>
<label>
Boolean checkbox
<input
{...form.getInputProps("regularCheckbox", {
type: "checkbox",
})}
/>
</label>
Setting default values
Checkboxes can be set using either a boolean
or a string[]
as the default value.
If you use a string[]
, the checkbox will be checked if the checkbox's value
prop
is in the array.
Observing / setting values
RVF will keep the value returned from form.value("myCheckbox")
consistent
with the type you provided as the default value.
If you don't set a default value, then it will return a boolean
.
Validating
Single checkboxes
Checkboxes commonly trip people up.
The way checkboxes are represented in FormData
on form submission is like this:
- A checked checkbox will be in the FormData as the value of its
value
prop. - If there is no value prop, then the checkbox will be in the FormData as
"on"
. - An unchecked checkbox will not be in the FormData at all.
Even if you used a boolean
as the default value, you're validator should expect to
receive "on" | undefined
.
// This will be "on" or undefined
<input type="checkbox" name="myCheckbox" />
// This will be "hello" or undefined
<input
type="checkbox"
name="checkboxWithValue"
value="hello"
/>
Checkbox groups
Checkbox group add an extra layer of complication here, because the FormData doesn't tell us how many checkboxes there are, just which ones are checked. RVF handles that like this:
- If only one checkbox is checked, the value your validator receives is a
string
. - If multiple checkboxes are checked, then the value is a
string[]
.
This means that your validator should expect to receive undefined | string | string[]
.
If you're using zod
, you can handle this case easily using
repeatable
from zod-form-data
.
// The value of this group could be
// - A single string ("value1")
// - An array of strings (["value1", "value2"])
// - undefined
<input type="checkbox" name="group" value="value1" />
<input type="checkbox" name="group" value="value2" />
<input type="checkbox" name="group" value="value3" />
Radio groups
These are much simpler than checkboxes.
Usage
Call getInputProps
for each radio input and pass in a type of "radio"
and the value
of the radio.
<label>
Value 1
<input
{...form.getInputProps("myRadio", {
type: "radio",
value: "value1"
})}
/>
</label>
<label>
Value 2
<input
{...form.getInputProps("myRadio", {
type: "radio",
value: "value2"
})}
/>
</label>
Setting default values
You can only set the default value using a string
.
Observing / setting values
The value returned will always be a string
, unless no radio is checked at all, in which case it will be undefined
.
Validating
Like when observing the values, the value will always be a string
. The exception is when no radio is checked at all, in which case it will be undefined
.
Selects
Usage
Select elements work naturally with getInputProps
with no other considerations.
// single select
<select {...form.getInputProps("mySelect")}>
<option value="foo">Foo</option>
<option value="bar">Bar</option>
<option value="baz">Baz</option>
</select>
// multi select
<select {...form.getInputProps("mySelect", { multiple: true })}>
<option value="foo">Foo</option>
<option value="bar">Bar</option>
<option value="baz">Baz</option>
</select>
Setting default values
Single selects can be set using a string
and multi selects can be set using an array
of string
.
Observing / setting values
Just like with default values, single selects will always be a string
and multi selects will always be an array
of string
.
Validating
This one has a gotcha. When the FormData
is submitted, it doesn't include any information about whether or not the select is a multi-select.
Therefore, RVF works around this like this:
- If one option is selected, the value will be a
string
. - If multiple options are selected, the value will be an
string[]
.
If you're using zod
, you can handle this case easily using
repeatable
from zod-form-data
.
File inputs
File inputs actually pretty inflexible. Try running this code to see what happens. If you do that, you might see an error like this:
This input element accepts a filename, which may only be programmatically set to the empty string.
const FileInputTest = () => {
const inputRef = useRef<HTMLInputElement>(null);
return <input type="file" defaultValue="myFile.txt" />;
}
Unfortunately, this means we can't set the default value of a file input
or modify it with setValue
, unless that value is an empty string (""
).
Relevant types
SingleFileInputValue
null | File
MultiFileInputValue
null | File[]
Setting default values
It isn't possible to set the default value of a file input.
Using setValue
You can use setValue
if and only if the value you pass in is an empty string (""
) or null
.
This will clear the file input.
Observing the value
Oberving a file input value works.
- If the input is set to
multiple
, then the value will have the typenull | File[]
. - If the input is not set to
multiple
, then the value will have the typenull | File
.
Other types
We've covered the most common input types, but there are many more types of inputs out there.
In all other cases, RVF will treat the value as a string
.