This content originally appeared on DEV Community and was authored by Damian Sire (Dev)
Form management in Angular, especially with the arrival of Signal-based forms, is constantly evolving to improve ergonomics and the overall developer experience (DX).
A subtle but incredibly welcome change is the simplification in how we define custom validation errors.
What previously required a utility function like customError to wrap our error object, now allows us to return a plain JavaScript object (POJO) directly.
This change reduces boilerplate, simplifies the API, and makes creating validators much more intuitive.
Note: The
form()andvalidate()APIs discussed here are part of the new Signal-based forms system.
You can explore the implementation details in the Angular PR:
PR #64339 — Simplify Custom Errors
The Key Change: Before and After
Let’s look at a direct comparison — the core of this improvement.
Before
Previously, to indicate a custom validation error, we often needed to import and use a utility function to wrap our error and ensure it was correctly typed and recognized by the forms system.
import { customError } from 'some-forms-library';
const cat = signal('meow');
const f = form(
cat,
(p) => {
validate(p, () => {
// We needed the customError wrapper
if (p() === 'meow') {
return customError({ kind: 'i am a custom error' });
}
return null;
});
});
Simplified Validator API in Angular Signal Forms
With the new simplified API, the validation engine is smart enough to understand a simple object as an error response.
If the validator function returns an object, it’s an error.
If it returns null or undefined, it’s valid.
const cat = signal('meow');
const f = form(
cat,
(p) => {
validate(p, () => {
// We simply return the error object
if (p() === 'meow') {
return { kind: 'iAmACustomError' }; // Much cleaner!
}
return null; // Valid
});
});
Why Does This Change Matter?
Less Boilerplate
You no longer need to import customError in every validator file.
More Intuitive API
The flow feels natural — “Is there an error? If so, return the error object.”
Easier Testing
Mocking or stubbing a validator is trivial when it just returns an object.
Consistency
It aligns with how synchronous validators work in traditional reactive forms (Validators.required returns { required: true }).
Final Thoughts
While this might seem like a small change, it’s a perfect example of Angular’s ongoing effort to make APIs simpler, more consistent, and more developer-friendly.
Cleaner validators mean fewer imports, less friction, and a smoother DX overall — especially as Signal-based forms continue to mature.
This content originally appeared on DEV Community and was authored by Damian Sire (Dev)