Validators.maxLength()
Quick Start (30 seconds)
const form = Forms.create(
{
username: '',
bio: '',
tweet: ''
},
{
username: Forms.v.maxLength(20, 'Username must be 20 characters or less'),
bio: Forms.v.maxLength(500, 'Bio must be 500 characters or less'),
tweet: Forms.v.maxLength(280, 'Tweet must be 280 characters or less')
}
);
// Too long
form.setValue('username', 'thisusernameiswaytoolong');
form.validateField('username');
console.log(form.getError('username')); // "Username must be 20 characters or less"
// Valid length
form.setValue('username', 'validuser');
form.validateField('username');
console.log(form.hasError('username')); // falseWhat just happened? Validators.maxLength() ensured the field doesn't exceed the maximum character limit!
What is Validators.maxLength()?
Validators.maxLength() is a built-in validator that enforces maximum length constraints.
Simply put, it prevents users from entering more than N characters, returning an error when exceeded.
Key characteristics:
- ✅ Validates maximum length
- ✅ Works with strings, arrays
- ✅ Custom error message
- ✅ Allows empty values
- ✅ Commonly used for character limits
Syntax
// Create maxLength validator
const validator = Forms.v.maxLength(100, 'Must be 100 characters or less');
// Use in form
const form = Forms.create(
{ bio: '' },
{ bio: Forms.v.maxLength(500, 'Bio too long (max 500 chars)') }
);
// Default message
const validator = Forms.v.maxLength(50); // "Must be 50 characters or less"Parameters:
max(number) - Maximum allowed lengthmessage(string, optional) - Custom error message
Returns: function(value) => string - Validator function
Why Does This Exist?
The Challenge
// ❌ Manual max length validation - repetitive
const form = Forms.create(
{
username: '',
bio: '',
comment: ''
},
{
username: (value) => {
if (value && value.length > 20) {
return 'Username too long';
}
return '';
},
bio: (value) => {
if (value && value.length > 500) {
return 'Bio too long';
}
return '';
},
comment: (value) => {
if (value && value.length > 1000) {
return 'Comment too long';
}
return '';
}
}
);The Solution
// ✅ Clean and consistent
const form = Forms.create(
{
username: '',
bio: '',
comment: ''
},
{
username: Forms.v.maxLength(20, 'Username too long'),
bio: Forms.v.maxLength(500, 'Bio too long'),
comment: Forms.v.maxLength(1000, 'Comment too long')
}
);How Does It Work?
function maxLength(max, message) {
const defaultMessage = `Must be ${max} characters or less`;
return function(value) {
if (!value) return ''; // Allow empty
const length = value.length;
if (length > max) {
return message || defaultMessage;
}
return '';
};
}Basic Usage
Example 1: Twitter-Style Character Limit
const form = Forms.create(
{ tweet: '' },
{ tweet: Forms.v.maxLength(280, 'Tweet must be 280 characters or less') }
);
effect(() => {
const length = form.values.tweet.length;
const remaining = 280 - length;
charCounter.textContent = remaining;
charCounter.className = remaining < 0 ? 'over-limit' : '';
});Example 2: Username Length Limit
const form = Forms.create(
{ username: '' },
{ username: Forms.v.maxLength(15, 'Username too long (max 15 characters)') }
);Example 3: Bio with Min and Max
const form = Forms.create(
{ bio: '' },
{
bio: Forms.v.combine(
Forms.v.minLength(10, 'Bio must be at least 10 characters'),
Forms.v.maxLength(500, 'Bio must be 500 characters or less')
)
}
);Example 4: Text Input with Hard Limit
const form = Forms.create(
{ description: '' },
{ description: Forms.v.maxLength(200) }
);
// Prevent typing beyond limit
descriptionInput.addEventListener('input', (e) => {
if (e.target.value.length > 200) {
e.target.value = e.target.value.substring(0, 200);
}
form.handleChange(e);
});Example 5: Array Size Limit
const form = Forms.create(
{ tags: [] },
{
tags: (value) => {
if (value && value.length > 5) {
return 'Maximum 5 tags allowed';
}
return '';
}
}
);Advanced Patterns
Pattern 1: Character Counter with Warning Zones
const form = Forms.create(
{ message: '' },
{ message: Forms.v.maxLength(500) }
);
effect(() => {
const length = form.values.message.length;
const max = 500;
const remaining = max - length;
charCounter.textContent = `${length} / ${max}`;
if (remaining < 0) {
charCounter.className = 'over-limit';
} else if (remaining < 50) {
charCounter.className = 'warning';
} else if (remaining < 100) {
charCounter.className = 'caution';
} else {
charCounter.className = 'safe';
}
});Pattern 2: Auto-Truncate on Paste
const form = Forms.create(
{ bio: '' },
{ bio: Forms.v.maxLength(500) }
);
bioInput.addEventListener('paste', (e) => {
e.preventDefault();
const pastedText = e.clipboardData.getData('text');
const truncated = pastedText.substring(0, 500);
form.setValue('bio', truncated);
bioInput.value = truncated;
if (pastedText.length > 500) {
showNotification(`Text truncated to ${500} characters`);
}
});Pattern 3: Progressive Warning System
const form = Forms.create(
{ comment: '' },
{ comment: Forms.v.maxLength(1000) }
);
effect(() => {
const length = form.values.comment.length;
const max = 1000;
const percentage = (length / max) * 100;
warningDiv.style.display = 'none';
if (percentage >= 100) {
warningDiv.textContent = '⛔ Maximum length reached';
warningDiv.className = 'error';
warningDiv.style.display = 'block';
} else if (percentage >= 90) {
warningDiv.textContent = '⚠️ Approaching character limit';
warningDiv.className = 'warning';
warningDiv.style.display = 'block';
} else if (percentage >= 75) {
warningDiv.textContent = 'ℹ️ Getting close to limit';
warningDiv.className = 'info';
warningDiv.style.display = 'block';
}
});Pattern 4: Max Length with Word Count
function maxWords(maxCount, message) {
return (value) => {
if (!value) return '';
const words = value.trim().split(/\s+/).filter(w => w.length > 0);
if (words.length > maxCount) {
return message || `Maximum ${maxCount} words allowed`;
}
return '';
};
}
const form = Forms.create(
{ essay: '' },
{ essay: maxWords(500, 'Essay must be 500 words or less') }
);
effect(() => {
const words = form.values.essay.trim().split(/\s+/).filter(w => w.length > 0);
wordCounter.textContent = `${words.length} / 500 words`;
});Pattern 5: Contextual Max Length Messages
function smartMaxLength(max, fieldName) {
return (value) => {
if (!value) return '';
const length = value.length;
if (length > max) {
const over = length - max;
if (over === 1) {
return `${fieldName} is 1 character too long`;
} else if (over <= 10) {
return `${fieldName} is ${over} characters too long`;
} else {
return `${fieldName} exceeds maximum by ${over} characters (max ${max})`;
}
}
return '';
};
}
const form = Forms.create(
{ bio: '' },
{ bio: smartMaxLength(200, 'Bio') }
);Pattern 6: Visual Progress Bar
const form = Forms.create(
{ description: '' },
{ description: Forms.v.maxLength(300) }
);
effect(() => {
const length = form.values.description.length;
const max = 300;
const percentage = Math.min(100, (length / max) * 100);
progressBar.style.width = `${percentage}%`;
if (percentage > 100) {
progressBar.classList.add('over-limit');
} else if (percentage > 90) {
progressBar.classList.add('warning');
} else {
progressBar.classList.remove('over-limit', 'warning');
}
});Pattern 7: Soft Limit with Hard Limit
const SOFT_LIMIT = 200;
const HARD_LIMIT = 250;
const form = Forms.create(
{ post: '' },
{ post: Forms.v.maxLength(HARD_LIMIT, `Maximum ${HARD_LIMIT} characters`) }
);
effect(() => {
const length = form.values.post.length;
if (length > SOFT_LIMIT && length <= HARD_LIMIT) {
softWarning.textContent = `⚠️ Recommended limit is ${SOFT_LIMIT} characters`;
softWarning.style.display = 'block';
} else {
softWarning.style.display = 'none';
}
});Pattern 8: Disable Submit When Over Limit
const form = Forms.create(
{ message: '' },
{ message: Forms.v.maxLength(500) }
);
effect(() => {
const isOverLimit = form.values.message.length > 500;
submitButton.disabled = isOverLimit;
if (isOverLimit) {
submitButton.title = 'Message exceeds character limit';
} else {
submitButton.title = '';
}
});Pattern 9: Emoji-Aware Length Validation
function maxLengthUnicode(max, message) {
return (value) => {
if (!value) return '';
// Count Unicode code points (handles emojis correctly)
const length = Array.from(value).length;
if (length > max) {
return message || `Must be ${max} characters or less`;
}
return '';
};
}
const form = Forms.create(
{ status: '' },
{ status: maxLengthUnicode(140, 'Status too long') }
);
// "Hello 👋🌍" = 8 code points (not 10)Pattern 10: Dynamic Max Length Based on Plan
const characterLimits = {
free: 100,
basic: 500,
premium: 2000,
enterprise: Infinity
};
function createFormForPlan(planType) {
const maxChars = characterLimits[planType];
return Forms.create(
{ bio: '' },
{
bio: maxChars === Infinity
? () => '' // No limit for enterprise
: Forms.v.maxLength(maxChars, `Bio limit: ${maxChars} characters`)
}
);
}
const freeUserForm = createFormForPlan('free'); // 100 char limit
const premiumUserForm = createFormForPlan('premium'); // 2000 char limitCommon Pitfalls
Pitfall 1: Not Preventing Input Beyond Limit
// ❌ Validation only, user can still type beyond limit
const form = Forms.create(
{ tweet: '' },
{ tweet: Forms.v.maxLength(280) }
);
// ✅ Enforce hard limit on input
tweetInput.addEventListener('input', (e) => {
if (e.target.value.length > 280) {
e.target.value = e.target.value.substring(0, 280);
}
form.handleChange(e);
});Pitfall 2: Not Showing Remaining Characters
// ❌ User doesn't know how many characters left
errorDiv.textContent = 'Too long';
// ✅ Show helpful counter
effect(() => {
const remaining = 280 - form.values.tweet.length;
counter.textContent = `${remaining} characters remaining`;
});Pitfall 3: Inconsistent Limits Across Platform
// ❌ Different limits in different places
// Frontend: 500 chars
// Backend API: 300 chars
// ✅ Use shared constant
const MAX_BIO_LENGTH = 500;
// Frontend
Forms.v.maxLength(MAX_BIO_LENGTH)
// Backend should use same constantPitfall 4: Not Handling Emoji/Unicode
// ❌ Regular length counts UTF-16 units
'👨👩👧👦'.length // 11 (not 1!)
// ✅ Use Unicode-aware counting
Array.from('👨👩👧👦').length // 7 (better, but still complex)
// For accurate emoji counting, use library like grapheme-splitterPitfall 5: No Visual Feedback
// ❌ Silent validation, user confused
Forms.v.maxLength(100)
// ✅ Always show character counter
effect(() => {
const length = form.values.field.length;
charCounter.textContent = `${length} / 100`;
});Summary
Key Takeaways
- Enforces maximum length - prevents exceeding character/item limit
- Allows empty values - combine with
required()if needed - Works with strings and arrays - checks
.lengthproperty - Should show counter - users need to know their limit
- Common for UX - prevents data truncation
When to Use maxLength()
✅ Use for:
- Tweet/post character limits
- Username length constraints
- Bio/description fields
- Comment sections
- SMS-length messages
❌ Don't use when:
- No practical length limit
- Need exact length (use pattern)
- Validating numbers (use
max()instead)
Common Max Length Values
| Field | Max Length | Reason | | -| | --| | Username | 15-20 | Display/readability | | Tweet | 280 | Platform standard | | Bio | 500-2000 | Meaningful content | | Comment | 1000-5000 | Discussion depth | | SMS | 160 | Technical limit | | Email subject | 78 | Email standard |
Related Validators
Validators.minLength()- Minimum lengthValidators.pattern()- Exact length with regexValidators.combine()- Min + Max rangeValidators.custom()- Complex length rules
One-Line Rule
Validators.maxLength(max, message)creates a validator that ensures a value doesn't exceed the specified number of characters or items, preventing data loss and enforcing UX constraints.
What's Next?
- Combine with
minLength()for length ranges - Add visual character counters
- Implement auto-truncation on paste