form.values
Quick Start (30 seconds)
// Create a form
const form = Forms.create({
username: '',
email: '',
age: ''
});
// Read values
console.log(form.values);
// { username: '', email: '', age: '' }
// Update a value (use setValue method!)
form.setValue('username', 'alice');
console.log(form.values);
// { username: 'alice', email: '', age: '' }
// Access individual values
console.log(form.values.username); // 'alice'
console.log(form.values.email); // ''What just happened? form.values is a reactive object containing all your form field values. Read from it anytime, but update through setValue() to trigger validation!
What is form.values?
form.values is a reactive object property that stores the current values of all your form fields.
Simply put, it's where the actual form data lives. When you type into a field, that data is stored in form.values. When you submit the form, you send form.values to your server.
Think of form.values as the data container for your form - like a basket holding all the information the user has entered.
Syntax
Reading Values
// Read the entire values object
const allValues = form.values;
// Read a specific field
const username = form.values.username;
const email = form.values.email;Updating Values (Don't Do This!)
// ❌ WRONG - Don't mutate directly
form.values.username = 'alice';
// ✅ CORRECT - Use setValue()
form.setValue('username', 'alice');Type: Object (read/write, but use methods to write)
Why Does This Exist?
The Problem Without a Central Values Store
Imagine managing form data without a central object:
// Manual form data management (messy!)
let username = '';
let email = '';
let password = '';
let age = '';
let bio = '';
// Update manually
function updateUsername(value) {
username = value;
validateUsername(value);
updateUI();
}
function updateEmail(value) {
email = value;
validateEmail(value);
updateUI();
}
// Submit manually
function handleSubmit() {
const data = {
username: username,
email: email,
password: password,
age: age,
bio: bio
};
sendToServer(data);
}
// Get current values manually
function getCurrentValues() {
return {
username: username,
email: email,
password: password,
age: age,
bio: bio
};
}What's the Real Issue?
Scattered Variables
↓
Hard to Track
↓
Manual Serialization
↓
Easy to Forget Fields
↓
Bugs EverywhereProblems: ❌ Values scattered across multiple variables ❌ Have to manually collect them for submission ❌ Easy to forget a field when submitting ❌ Hard to inspect all values at once ❌ Can't pass "all values" to functions easily ❌ No single source of truth
The Solution with form.values
// Reactive form with centralized values
const form = Forms.create({
username: '',
email: '',
password: '',
age: '',
bio: ''
});
// Update using methods
form.setValue('username', 'alice');
form.setValue('email', 'alice@example.com');
// All values in one place
console.log(form.values);
// {
// username: 'alice',
// email: 'alice@example.com',
// password: '',
// age: '',
// bio: ''
// }
// Submit is easy
const result = await form.submit();
// Automatically uses form.values
// Or access directly
await fetch('/api/profile', {
method: 'POST',
body: JSON.stringify(form.values)
});What Just Happened?
form.values
↓
Single Source of Truth
↓
Easy to Read/Inspect
↓
Automatic Serialization
↓
Clean, Bug-Free CodeBenefits: ✅ All values in one object ✅ Single source of truth ✅ Easy to inspect entire form state ✅ Automatic serialization for submission ✅ Can pass to validators that need multiple fields ✅ Reactive - updates automatically trigger effects
Mental Model
Think of form.values like a form clipboard that holds all the user's answers:
Without form.values (Sticky Notes Everywhere)
Question 1: Name?
[Sticky note on desk: "Alice"]
Question 2: Email?
[Sticky note on wall: "alice@example.com"]
Question 3: Age?
[Sticky note on monitor: "25"]
You: "What were all the answers again?"
*Frantically collects sticky notes from everywhere*With form.values (Single Clipboard)
[Clipboard with all answers:]
┌─────────────────────────┐
│ Name: Alice │
│ Email: alice@example.com│
│ Age: 25 │
└─────────────────────────┘
You: "What were all the answers?"
*Glances at clipboard - done!*Key Insight: form.values keeps all your form data organized in one central, easily accessible location.
How Does It Work?
Under the Hood
When you create a form, form.values is initialized with your initial values:
Forms.create({ username: '', email: '' })
↓
Creates reactive state
↓
{
values: { username: '', email: '' }, ← form.values
errors: {},
touched: {},
isSubmitting: false
}
↓
Returns form object with values propertyVisual Structure
form (reactive object)
├── values
│ ├── username: ''
│ ├── email: ''
│ └── password: ''
├── errors
│ └── (error messages)
├── touched
│ └── (touched flags)
└── methods
├── setValue()
├── setValues()
└── submit()Reactivity
form.values is reactive, meaning:
// Create effect that watches values
effect(() => {
console.log('Values changed:', form.values);
});
// When you update...
form.setValue('username', 'alice');
// Console logs: "Values changed: { username: 'alice', ... }"
// The effect runs automatically!Basic Usage
Example 1: Reading All Values
const form = Forms.create({
firstName: '',
lastName: '',
email: ''
});
// Fill out the form
form.setValue('firstName', 'Alice');
form.setValue('lastName', 'Smith');
form.setValue('email', 'alice@example.com');
// Read all values
console.log(form.values);
// {
// firstName: 'Alice',
// lastName: 'Smith',
// email: 'alice@example.com'
// }
// Stringify for API
const json = JSON.stringify(form.values);
console.log(json);
// '{"firstName":"Alice","lastName":"Smith","email":"alice@example.com"}'Example 2: Reading Individual Values
const form = Forms.create({
username: 'alice',
email: 'alice@example.com',
bio: 'Hello world'
});
// Access specific values
const username = form.values.username;
console.log(username); // 'alice'
const email = form.values.email;
console.log(email); // 'alice@example.com'
// Use in expressions
if (form.values.bio.length > 100) {
console.log('Bio is too long!');
}Example 3: Displaying Values in UI
const form = Forms.create({
name: '',
email: ''
});
// Bind to DOM with reactivity
effect(() => {
document.getElementById('name-display').textContent = form.values.name;
document.getElementById('email-display').textContent = form.values.email;
});
// When values change, UI updates automatically
form.setValue('name', 'Alice');
// DOM updates: "Alice"
form.setValue('email', 'alice@example.com');
// DOM updates: "alice@example.com"Example 4: Checking if Form is Empty
const form = Forms.create({
search: '',
filter: ''
});
// Check if all values are empty
function isFormEmpty() {
return Object.values(form.values).every(val => !val);
}
console.log(isFormEmpty()); // true
form.setValue('search', 'hello');
console.log(isFormEmpty()); // falseReading Values
Pattern 1: Destructure Values
const form = Forms.create({
username: 'alice',
email: 'alice@example.com',
age: 25
});
// Destructure for easier access
const { username, email, age } = form.values;
console.log(username); // 'alice'
console.log(email); // 'alice@example.com'
console.log(age); // 25Pattern 2: Iterate Over Values
const form = Forms.create({
name: 'Alice',
email: 'alice@example.com',
phone: '555-1234'
});
// Log all field names and values
Object.entries(form.values).forEach(([field, value]) => {
console.log(`${field}: ${value}`);
});
// name: Alice
// email: alice@example.com
// phone: 555-1234Pattern 3: Check Specific Values
const form = Forms.create({
termsAccepted: false,
newsletter: false,
age: ''
});
// Check boolean values
if (form.values.termsAccepted) {
console.log('Terms accepted!');
}
// Check if field has value
if (form.values.age) {
console.log('Age provided:', form.values.age);
}Pattern 4: Use in Computed Properties
const form = Forms.create({
firstName: '',
lastName: ''
});
// Compute full name from values
form.computed('fullName', function() {
return `${this.values.firstName} ${this.values.lastName}`.trim();
});
form.setValue('firstName', 'Alice');
form.setValue('lastName', 'Smith');
console.log(form.fullName); // 'Alice Smith'Updating Values
✅ Correct Way: Use setValue()
const form = Forms.create({
username: '',
email: ''
});
// Use setValue method
form.setValue('username', 'alice');
form.setValue('email', 'alice@example.com');
console.log(form.values);
// { username: 'alice', email: 'alice@example.com' }
// Benefits:
// ✅ Triggers validation
// ✅ Marks field as touched
// ✅ Updates errors automatically
// ✅ Reactive - triggers effects✅ Correct Way: Use setValues() for Multiple
const form = Forms.create({
firstName: '',
lastName: '',
email: ''
});
// Update multiple values at once
form.setValues({
firstName: 'Alice',
lastName: 'Smith',
email: 'alice@example.com'
});
console.log(form.values);
// All three fields updated!❌ Wrong Way: Direct Mutation
const form = Forms.create({
username: '',
email: ''
});
// DON'T DO THIS!
form.values.username = 'alice';
form.values.email = 'alice@example.com';
// Problems:
// ❌ Doesn't trigger validation
// ❌ Doesn't mark as touched
// ❌ Errors won't update
// ❌ May not trigger effects properlyAlways use the methods (setValue, setValues) to update values!
Advanced Patterns
Pattern 1: Pre-fill Form from API
// Fetch user data from API
const userData = await fetch('/api/user/123').then(r => r.json());
// { name: 'Alice', email: 'alice@example.com', bio: 'Developer' }
// Create form with API data
const form = Forms.create({
name: userData.name,
email: userData.email,
bio: userData.bio
});
console.log(form.values);
// { name: 'Alice', email: 'alice@example.com', bio: 'Developer' }
// Form is pre-filled!Pattern 2: Transform Values Before Submission
const form = Forms.create({
firstName: '',
lastName: '',
email: ''
});
form.setValues({
firstName: 'alice',
lastName: 'smith',
email: 'ALICE@EXAMPLE.COM'
});
// Transform values before sending
const transformedValues = {
firstName: form.values.firstName.charAt(0).toUpperCase() +
form.values.firstName.slice(1),
lastName: form.values.lastName.charAt(0).toUpperCase() +
form.values.lastName.slice(1),
email: form.values.email.toLowerCase()
};
console.log(transformedValues);
// {
// firstName: 'Alice',
// lastName: 'Smith',
// email: 'alice@example.com'
// }Pattern 3: Partial Updates
const form = Forms.create({
name: 'Alice',
email: 'alice@example.com',
phone: '555-1234',
address: '123 Main St'
});
// Update only changed fields
const changedFields = {
email: 'newemail@example.com'
};
form.setValues(changedFields);
console.log(form.values);
// {
// name: 'Alice', ← unchanged
// email: 'newemail@example.com', ← updated
// phone: '555-1234', ← unchanged
// address: '123 Main St' ← unchanged
// }Pattern 4: Watch Values for Changes
const form = Forms.create({
search: '',
filter: ''
});
// Watch for any value change
effect(() => {
console.log('Form values:', JSON.stringify(form.values));
// Could trigger search API call
if (form.values.search) {
searchAPI(form.values.search, form.values.filter);
}
});
// Triggers effect
form.setValue('search', 'hello');
// Console: 'Form values: {"search":"hello","filter":""}'Pattern 5: Compare with Initial Values
const initialData = {
name: 'Alice',
email: 'alice@example.com'
};
const form = Forms.create(initialData);
// User makes changes
form.setValue('email', 'newemail@example.com');
// Check what changed
function getChangedFields() {
const changed = {};
Object.keys(form.values).forEach(key => {
if (form.values[key] !== initialData[key]) {
changed[key] = form.values[key];
}
});
return changed;
}
console.log(getChangedFields());
// { email: 'newemail@example.com' }
// Only email changed!Pattern 6: Conditional Fields Based on Values
const form = Forms.create({
accountType: 'personal',
companyName: '',
taxId: ''
});
// Show/hide fields based on accountType
effect(() => {
const showCompanyFields = form.values.accountType === 'business';
document.getElementById('company-fields').style.display =
showCompanyFields ? 'block' : 'none';
});
// Switch to business
form.setValue('accountType', 'business');
// Company fields now visible!Pattern 7: Auto-save on Value Change
const form = Forms.create({
title: '',
content: ''
});
// Auto-save draft after any change
let saveTimeout;
effect(() => {
// Watch all values
const _ = form.values;
// Debounce save
clearTimeout(saveTimeout);
saveTimeout = setTimeout(async () => {
await fetch('/api/save-draft', {
method: 'POST',
body: JSON.stringify(form.values)
});
console.log('Draft saved!');
}, 1000);
});
form.setValue('title', 'My Post');
// Saves after 1 second of inactivityCommon Pitfalls
Pitfall 1: Direct Mutation Instead of Using Methods
❌ Wrong:
const form = Forms.create({ username: '' });
// Direct mutation
form.values.username = 'alice';
// Validation doesn't run!
console.log(form.touched.username); // undefined
console.log(form.errors.username); // Not validated✅ Correct:
const form = Forms.create({ username: '' });
// Use setValue
form.setValue('username', 'alice');
// Validation runs automatically
console.log(form.touched.username); // true ✅
console.log(form.errors.username); // Validated ✅Pitfall 2: Expecting Values to be Undefined
❌ Wrong:
const form = Forms.create({
username: '',
email: ''
});
if (form.values.age) {
// This never runs!
console.log('Age exists');
}Why? form.values only contains fields defined in initialValues. The field age doesn't exist!
✅ Correct:
const form = Forms.create({
username: '',
email: '',
age: '' // Define it!
});
if (form.values.age) {
console.log('Age exists');
}Pitfall 3: Modifying Values Object Directly
❌ Wrong:
const form = Forms.create({ tags: [] });
// Mutating array directly
form.values.tags.push('javascript');
// May not trigger reactivity properly!✅ Correct:
const form = Forms.create({ tags: [] });
// Create new array
const newTags = [...form.values.tags, 'javascript'];
form.setValue('tags', newTags);
// Reactivity works correctly ✅Pitfall 4: Forgetting Values are Reactive
❌ Wrong:
const form = Forms.create({ count: 0 });
// Capture value
const count = form.values.count;
// Later...
form.setValue('count', 5);
console.log(count); // Still 0!Why? You captured the value, not a reference to the reactive property.
✅ Correct:
const form = Forms.create({ count: 0 });
form.setValue('count', 5);
// Read when needed
console.log(form.values.count); // 5 ✅Pitfall 5: Assuming Nested Objects are Deep
❌ Wrong:
const form = Forms.create({
user: {
name: '',
email: ''
}
});
// This won't work as expected
form.setValue('user.name', 'Alice');Current implementation works best with flat structures.
✅ Workaround:
const form = Forms.create({
userName: '',
userEmail: ''
});
form.setValue('userName', 'Alice');
form.setValue('userEmail', 'alice@example.com');Or update the entire object:
const form = Forms.create({
user: { name: '', email: '' }
});
form.setValue('user', {
name: 'Alice',
email: 'alice@example.com'
});Summary
Key Takeaways
form.valuesis a reactive object containing all your form field values in one place.It's the single source of truth for your form data - everything you need is in one object.
Read from it freely - access individual values or the entire object anytime.
Update using methods - always use
setValue()orsetValues(), never mutate directly.It's initialized from the initial values passed to
Forms.create()- only defined fields exist.It's reactive - changes automatically trigger validation, effects, and UI updates.
Perfect for submission -
form.valuesis exactly what you send to your API.Use it in validators - cross-field validation receives all values as a parameter.
Watch it for auto-save - changes to values can trigger automatic persistence.
It serializes easily -
JSON.stringify(form.values)works perfectly.
One-Line Rule
form.valuesis your form's data container - read from it anytime, but always update throughsetValue()to maintain reactivity and validation.
What's Next?
- Learn about
form.errorsto track validation errors - Explore
form.touchedto know which fields the user interacted with - Master
form.isValidto check overall form validity - Discover form methods like
setValue(),setValues(), andsubmit()