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

Understanding Vest's State

One of Vest's most powerful features is its stateful validation. Unlike schema-based validators that start fresh every time, Vest remembers previous results and merges them intelligently.

Why Stateful Validation?

Imagine a form with 10 fields. When a user updates just the "username" field, you have two options:

  1. Re-validate everything - Slow, and might flash errors on untouched fields
  2. Validate only "username" - Fast, but you lose the state of other fields

Vest gives you the best of both worlds: validate one field, keep the full picture.

How It Works

Step 1: User fills "Password" field
└─→ suite.run() validates password
└─→ Result: { password: ✓ }

Step 2: User fills "Username" field
└─→ suite.focus({ only: 'username' }).run()
└─→ Vest runs ONLY username tests
└─→ Vest MERGES with previous password result
└─→ Result: { username: ?, password: ✓ } ← Full picture!

Step 3: User fixes username error
└─→ suite.focus({ only: 'username' }).run()
└─→ Result: { username: ✓, password: ✓ } ← Ready to submit!

This is why isValid() always gives you the complete answer - even when you only validated one field.

What Vest's State Does

  • Skipped field merge: When you focus on specific fields, Vest keeps the results of untouched fields.
  • Lagging async test blocking: If an old async test finishes after a new one started, Vest ignores the stale result.

When State Becomes a Problem

Stateful validation is great for forms, but sometimes you need to reset it.

Problem 1: Navigation in SPAs

If a user submits a form successfully, navigates away, then comes back - the form still shows "success" state from the previous submission.

Solution: Reset on mount

useEffect(() => {
suite.reset();
}, []);

Problem 2: Dynamic Fields

If you dynamically add/remove fields (like items in a cart), removed fields still exist in Vest's state and might cause isValid() to return false.

Solution: Remove the field

function handleRemoveItem(id) {
removeFromCart(id);
suite.remove(`item_${id}`);
}

State Management Methods

suite.reset()

Wipes all validation state. Use when:

  • User clicks "Clear form"
  • Component unmounts
  • Starting a new transaction
suite.reset();

suite.resetField(fieldName)

Clears just one field's results. Use when:

  • User clicks "clear" on an input
  • You want to remove errors without re-validating
suite.resetField('email');

suite.remove(fieldName)

Removes a field from state entirely. Use when:

  • A field is dynamically removed from the UI
  • You want isValid() to stop considering that field
suite.remove('couponCode');

Stateless Alternative: runStatic()

If you're on the server or don't need state merging, use runStatic():

// Server-side: fresh validation every request
app.post('/register', (req, res) => {
const result = suite.runStatic(req.body);
// State is immediately discarded
});

Summary

ScenarioSolution
Form reset / navigationsuite.reset()
Clear single fieldsuite.resetField('field')
Remove dynamic fieldsuite.remove('field')
Server-side validationsuite.runStatic()

Vest's statefulness is a feature, not a limitation. It's what makes incremental validation fast and accurate. When you need to escape it, the tools above give you full control.