Skip to main content
Version: 6.x (next)

Handling User Interaction

A common challenge in form validation is "noise control." You don't want to scream errors at a user before they've even touched a field.

Traditionally, libraries use an isDirty flag to track if a user has modified a field. Since Vest is UI-agnostic (it doesn't touch your DOM or listen to events), it doesn't track "dirty" state for you.

Instead, Vest provides two powerful tools to handle user interaction: isTested() and suite.only().

1. isTested(): The Vest Alternative to isDirty​

When you want to decide if you should show an error message, you usually want to know: "Has this field actually been validated yet?"

If a field hasn't been validated, it usually means the user hasn't interacted with it. Vest tracks this for you.

const result = suite.get();

// Only show errors if the field has actually been tested
const shouldShowError =
result.hasErrors('username') && result.isTested('username');

if (shouldShowError) {
renderError(result.getErrors('username'));
}

This pattern ensures that empty, untouched fields don't show "Required" errors when the form first loads.

2. Validating on Interaction with suite.only()​

When a user blurs a field or types, you often want to validate only that specific field, while keeping the rest of the form state intact.

Vest 6 introduces suite.only(). This tells Vest to run validations for specific fields, while skipping others.

// On Blur handler
function handleBlur(fieldName, formData) {
// 1. Tell Vest to validate ONLY the blurred field
// 2. Run the suite with the current data
suite.only(fieldName).run(formData);
}

Why use only()?​

  • Performance: It skips expensive tests (like async checks) for fields the user isn't touching.
  • User Experience: It updates the state for the current field without accidentally flagging other fields as "tested" or "invalid" before the user reaches them.
Real-World Pattern

Combine suite.only() with isTested() for the best UX:

  • Use only(fieldName) in your onBlur handler to validate only the current field
  • Use isTested(fieldName) when rendering to decide whether to show errors

Complete Example​

import suite from './validation';

function Form() {
const [formData, setFormData] = useState({});
const [result, setResult] = useState(suite.get());

const handleChange = e => {
const { name, value } = e.target;
setFormData(prev => ({ ...prev, [name]: value }));
};

const handleBlur = e => {
const { name } = e.target;
// Validate only the blurred field
const res = suite.only(name).run(formData);
setResult(res);
};

const handleSubmit = e => {
e.preventDefault();
// Validate all fields on submit
const res = suite.run(formData);
setResult(res);

if (res.isValid()) {
// Submit the form
}
};

// Only show error if field was tested
const showError = fieldName => {
return result.isTested(fieldName) && result.hasErrors(fieldName);
};

return (
<form onSubmit={handleSubmit}>
<input name="username" onChange={handleChange} onBlur={handleBlur} />
{showError('username') && <span>{result.getError('username')}</span>}

<button type="submit">Submit</button>
</form>
);
}

Summary​

GoalTraditional ApproachVest Approach
Did the user touch this?Check field.isDirtyCheck result.isTested('field')
Validate on BlurCall validateField('field')Call suite.only('field').run(data)

By combining isTested() (to hide premature errors) and suite.only() (to update specific fields), you get precise control over the user experience without tightly coupling your validation to the DOM.