Repository navigation
π₯ Breaking β field values can now be string[]
Value['value'], FieldError['value'], the value passed to a custom validation, and the values argument to a SchemaResolver widen from string to string | string[].
π Migration guide: v4 β v5
Do you need to change anything?
Probably not. Reading and rendering values still type-checks:
<span>{values.email?.value}</span>
{values.plan?.value === 'pro' && <ProBadge />}You need to narrow only where a value is passed somewhere that requires a string:
- const trimmed = values.email.value.trim()
+ const raw = values.email.value
+ const trimmed = typeof raw === 'string' ? raw.trim() : raw.join(',')TypeScript flags every such site, so the compiler is your migration checklist β nothing fails at runtime that compiled before.
Custom validations reading .length need no change β it means "non-empty" for both strings and arrays. String-specific methods (test, match, trim, toLowerCase) need a typeof guard.
The bundled Zod and Yup adapters need no changes. They pass values straight to the schema. If you wrote your own resolver and typed its parameter explicitly, widen it to Record<string, string | string[]>, and describe multi-selects as arrays in your schema (z.array(z.string()).min(1)).
Why
<select multiple> and <input type="file" multiple> reported only their first selection β every other selection was unrecoverable from values.
| Case | v4 | v5 |
|---|---|---|
<select multiple>, ts + go selected |
'ts' |
['ts', 'go'] |
| nothing selected | '' |
[] |
| after reset | '' |
[] |
Representing a list requires a list. Joining ('ts,go') or JSON-encoding would keep the type as string but push parsing onto every consumer and be ambiguous against literal values.
Only list-valued fields produce an array. Every other field type still produces a string, and runtime behaviour for single-value fields is unchanged.
<select multiple name="langs" required>
<option value="ts">TS</option>
<option value="go">Go</option>
</select>
// values.langs?.value β ['ts', 'go']A required list-valued field is invalid exactly when the list is empty, and a reset clears it to [] β so a field's value type never changes underneath you.
Also in this release
<input type="file" multiple> reports every file name, rather than just the first. Included here deliberately: it is the same problem, cost one extra branch, and bundling it avoids spending a second major on it later.
What's Changed
π₯ Breaking Change
- β¨ feat!: widen field values to
string | string[]for multi-value fields by @Randagio13 in #87
Full Changelog: v4.2.0...v5.0.0