You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
import * as v from "valibot";
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export function emptyStringOr<TSchema extends v.BaseSchema<any, any, any>>(schema: TSchema) {
return v.union([
v.literal(""),
schema
]);
}
It would allow a schema to accept either the literal "" (an empty string) or a value matching the supplied schema.
Why
Why
In modern React / Next.js form workflows, the distinction between controlled and uncontrolled components is critical. The built-in methods like optional() or nullable() in Valibot may lead devs to initialize form fields as undefined or null, which triggers the classic React warning:
“A component is changing an uncontrolled input to be controlled. This is likely caused by the value changing from undefined to a defined value…” 
Here’s how the wrong pattern happens:
A developer uses optional() on a string field, so the type allows string | undefined.
The React input’s value ends up being undefined initially, making the input uncontrolled.
Later the user types something, the value becomes a defined string → the input transitions to controlled → React throws a warning.
This can lead to subtle bugs, UI glitches or inconsistent form state.
By contrast, emptyStringOr(schema) gives you: a safe default of "" for no value (so the input remains a string, staying controlled), or a valid string per your schema. That avoids the uncontrolled→controlled switch entirely and aligns with best practices for controlled components: always supply a string (not undefined) to the value prop. 
Backward compatibility & Notes
Purely additive: no breaking changes.
Devs can continue using optional()/nullable() for scenarios where undefined or null are truly meaningful values—but in React inputs this often isn’t the case.
Clarify in the docs: emptyStringOr() does not transform "" into undefined—it simply allows "" as a valid value. If transformation is needed, the user should apply v.transform() on top.
Emphasize that this pattern is especially important in React forms where you want to avoid unintended uncontrolled→controlled transitions.
Example usage (React-form context)
export const PersonProfile = object({
firstName: pipe(string('El Nombre es requerido'), trim()),
phone: emptyStringOr(PhoneNumber),
phoneCountryCode: string(),
});
// In your React form:
// initial value: "" rather than undefined
// validation: either empty string (meaning “not filled”) or a non-empty string
Documentation snippet
emptyStringOr(schema): Schema
Accepts either the literal empty string "", or a value matching the given schema.
Why use it? In controlled React inputs you want the value prop to always be a string (to avoid uncontrolled ↔ controlled warnings). This helper makes that case explicit, rather than relying on undefined or null which may lead to UI or state inconsistencies.
Equivalent to: v.union([ v.literal(""), schema ]).
Call to Action
I propose we introduce emptyStringOr(...) in the next minor version of Valibot. I’m happy to open a PR containing full implementation + tests + doc updates.
reacted with thumbs up emoji reacted with thumbs down emoji reacted with laugh emoji reacted with hooray emoji reacted with confused emoji reacted with heart emoji reacted with rocket emoji reacted with eyes emoji
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
-
What
Add a new helper function:
It would allow a schema to accept either the literal "" (an empty string) or a value matching the supplied schema.
Why
Why
In modern React / Next.js form workflows, the distinction between controlled and uncontrolled components is critical. The built-in methods like optional() or nullable() in Valibot may lead devs to initialize form fields as undefined or null, which triggers the classic React warning:
“A component is changing an uncontrolled input to be controlled. This is likely caused by the value changing from undefined to a defined value…” 
Here’s how the wrong pattern happens:
By contrast, emptyStringOr(schema) gives you: a safe default of "" for no value (so the input remains a string, staying controlled), or a valid string per your schema. That avoids the uncontrolled→controlled switch entirely and aligns with best practices for controlled components: always supply a string (not undefined) to the value prop. 
Backward compatibility & Notes
Example usage (React-form context)
Documentation snippet
emptyStringOr(schema): Schema
Accepts either the literal empty string "", or a value matching the given schema.
Why use it? In controlled React inputs you want the value prop to always be a string (to avoid uncontrolled ↔ controlled warnings). This helper makes that case explicit, rather than relying on undefined or null which may lead to UI or state inconsistencies.
Equivalent to: v.union([ v.literal(""), schema ]).
Call to Action
I propose we introduce emptyStringOr(...) in the next minor version of Valibot. I’m happy to open a PR containing full implementation + tests + doc updates.
Beta Was this translation helpful? Give feedback.
All reactions