Reader Stacks

Password Confirmation Validation: Angular, JavaScript, and Laravel

The same requirement — password and confirm-password must match — implemented three different ways depending on where the validation actually needs to happen.

Password Confirmation Validation: Angular, JavaScript, and Laravel

Confirming that a password and its re-typed confirmation match is one of the most common form validation requirements, and it looks slightly different depending on the stack — Angular's reactive forms, plain JavaScript, and Laravel's server-side validation each handle it in their own idiomatic way.

Angular reactive forms (Angular 14+)

this.form = this.fb.group({
    password: ['', [Validators.required, Validators.minLength(8)]],
    confirmPassword: ['', Validators.required],
}, { validators: this.passwordsMatchValidator });

passwordsMatchValidator(group: FormGroup) {
    const password = group.get('password')?.value;
    const confirmPassword = group.get('confirmPassword')?.value;
    return password === confirmPassword ? null : { passwordMismatch: true };
}
Passwords do not match.

The matching check is attached as a group-level validator (the second argument to fb.group()), not on either individual field, since it needs to compare two fields against each other rather than validate one field in isolation.

Plain JavaScript

document.getElementById('signup-form').addEventListener('submit', function (event) {
    const password = document.getElementById('password').value;
    const confirmPassword = document.getElementById('confirm-password').value;

    if (password !== confirmPassword) {
        event.preventDefault();
        document.getElementById('password-error').textContent = 'Passwords do not match.';
    }
});

This client-side check improves the user experience by catching the mismatch instantly, but it's trivially bypassable from outside the browser — it's never a substitute for validating the same rule again on the server.

Laravel server-side validation

$request->validate([
    'password' => 'required|min:8|confirmed',
]);

Laravel's confirmed rule is a convention-based shortcut — it automatically checks the password field against a field named password_confirmation, with no separate validator method needed. The field naming convention ({field}_confirmation) is what makes this work; naming the confirmation input anything else means the rule won't find it.

Why the server-side check is the one that actually matters

Both the Angular and plain-JavaScript examples improve the experience by giving instant feedback, but neither is a security or data-integrity guarantee — a request can always be sent directly to the server bypassing any client-side JavaScript entirely, which is exactly why Laravel's confirmed rule (or equivalent server-side validation in any stack) is the one check that's actually mandatory, with client-side validation as a pure UX improvement layered on top.