setCustomValidity() and rendering the native Salesforce red error banner using reportValidity().
While standard attributes like required, minlength, and pattern handle basic constraints, real-world business forms often require dynamic, cross-field validation rules (such as validating email domain formats, custom password criteria, or comparing date ranges). Implementing platform-compliant client-side validation stops bad data before it hits Apex or database triggers.
Prerequisites
- A Salesforce Developer Edition org, Scratch Org, or Sandbox.
- Salesforce CLI (
sf) installed and authenticated. - Familiarity with JavaScript DOM querying and standard Lightning base components.
Step 1: Set Up the Component Structure
sf lightning generate component -n customValidationForm -d force-app/main/default/lwc --type lwc
Step 2: Build the Form HTML Template
In customValidationForm.html, place standard lightning-input elements and bind them with descriptive data-field identifiers:
<template>
<lightning-card title="Custom Form Validation Demo" icon-name="utility:form">
<div class="slds-p-around_medium">
<div class="slds-grid slds-gutters slds-wrap">
<div class="slds-col slds-size_1-of-1 slds-medium-size_1-of-2 slds-m-bottom_small">
<lightning-input
label="Username"
data-field="username"
value={formData.username}
onchange={handleInputChange}
placeholder="At least 5 alphanumeric characters"
required>
</lightning-input>
</div>
<div class="slds-col slds-size_1-of-1 slds-medium-size_1-of-2 slds-m-bottom_small">
<lightning-input
type="email"
label="Corporate Email"
data-field="email"
value={formData.email}
onchange={handleInputChange}
placeholder="user@company.com"
required>
</lightning-input>
</div>
</div>
<!-- Action Buttons -->
<div class="slds-m-top_medium slds-button-group" role="group">
<lightning-button
variant="brand"
label="Submit Registration"
onclick={handleSubmit}>
</lightning-button>
<lightning-button
variant="neutral"
label="Reset Form"
onclick={handleReset}>
</lightning-button>
</div>
</div>
</lightning-card>
</template>
Step 3: Implement Dynamic Custom Validation in JavaScript
In customValidationForm.js, query input elements, execute regex/business rules, set custom error strings, and trigger visual indicators via reportValidity():
import { LightningElement, track } from 'lwc';
import { ShowToastEvent } from 'lightning/platformShowToastEvent';
const USERNAME_REGEX = /^[a-zA-Z0-9_]{5,15}$/;
const EMAIL_REGEX = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;
export default class CustomValidationForm extends LightningElement {
@track formData = {
username: '',
email: ''
};
handleInputChange(event) {
const field = event.target.dataset.field;
this.formData[field] = event.target.value.trim();
// Clear custom error as user types to prevent sticky validation blocks
event.target.setCustomValidity('');
event.target.reportValidity();
}
validateAllFields() {
let isAllValid = true;
const usernameInput = this.template.querySelector('[data-field="username"]');
const emailInput = this.template.querySelector('[data-field="email"]');
// 1. Custom Username Validation
if (!this.formData.username) {
usernameInput.setCustomValidity('Username is required.');
isAllValid = false;
} else if (!USERNAME_REGEX.test(this.formData.username)) {
usernameInput.setCustomValidity(
'Username must be 5-15 characters and contain only letters, numbers, or underscores.'
);
isAllValid = false;
} else {
usernameInput.setCustomValidity('');
}
usernameInput.reportValidity();
// 2. Custom Email Validation
if (!this.formData.email) {
emailInput.setCustomValidity('Corporate email is required.');
isAllValid = false;
} else if (!EMAIL_REGEX.test(this.formData.email)) {
emailInput.setCustomValidity('Please enter a valid corporate email format (e.g. name@company.com).');
isAllValid = false;
} else if (this.formData.email.endsWith('@example.com')) {
emailInput.setCustomValidity('Public test domains like @example.com are not permitted.');
isAllValid = false;
} else {
emailInput.setCustomValidity('');
}
emailInput.reportValidity();
return isAllValid;
}
handleSubmit() {
const isValid = this.validateAllFields();
if (isValid) {
// Form is clean - execute Apex mutation or LDS createRecord here
this.showToast('Validation Success', 'Form passed all custom business rules!', 'success');
} else {
this.showToast('Validation Error', 'Please resolve the highlighted errors before submitting.', 'error');
}
}
handleReset() {
this.formData = { username: '', email: '' };
const allInputs = this.template.querySelectorAll('lightning-input');
allInputs.forEach(input => {
input.value = '';
input.setCustomValidity('');
input.reportValidity();
});
this.showToast('Reset Complete', 'Form inputs have been cleared.', 'info');
}
showToast(title, message, variant) {
this.dispatchEvent(new ShowToastEvent({ title, message, variant }));
}
}
setCustomValidity('Error message') sets the custom error on the component's internal validity state, but does not render the error message on the UI until you call reportValidity(). Furthermore, when the input becomes valid, you must explicitly pass an empty string setCustomValidity(''); otherwise, the field will remain permanently invalid.
- setCustomValidity(msg): Injects custom error strings into the component validity state. Pass
''to clear errors. - reportValidity(): Evaluates the element's validity state and draws or clears red SLDS error text on the screen.
- checkValidity(): Returns a boolean (
trueorfalse) without changing the UI. - Multi-Field Validation: Use
Array.reduce()acrossquerySelectorAllto evaluate entire forms in a single statement.
Step 4: Configure Metadata and Deploy
Update customValidationForm.js-meta.xml to expose the component in Lightning App Builder:
<?xml version="1.0" encoding="UTF-8"?>
<LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata">
<apiVersion>60.0</apiVersion>
<isExposed>true</isExposed>
<targets>
<target>lightning__AppPage</target>
<target>lightning__RecordPage</target>
<target>lightning__HomePage</target>
</targets>
</LightningComponentBundle>
# Deploy to default org
sf project deploy start
# Open target org
sf org open
- In Salesforce Setup, navigate to Lightning App Builder.
- Add
customValidationFormto a Record or App page, then save and activate. - Enter invalid inputs (e.g. short usernames or
test@example.com) to verify custom error messaging.
setCustomValidity() with reportValidity() provides native, accessible, and user-friendly client-side validation before sending data to server-side Apex controllers.