Custom Form Controls with Signal Forms In short: Signal Forms are stable in Angular 22. A custom control is now a component with a that implements . No , no provider, no . Layout components are one half of a library. Data entry is the other, and it is where the complexity lives. In the Dairy Logistics system, operators do not type a silo ID into a text box. They pick from a grid of silo cards showing live capacity, temperature, and status. That is a custom widget — and it needs to behave like a native inside a form, or nobody will use it. The problem, stated plainly Build the picker with your own inputs and outputs and it looks fine: And then: It does not work with the form system. Binding a form field to it fails. Every consumer writes the same wiring by hand, slightly differently. Validation state is lost. The form has no idea when the user touched it, so "please select a silo" either never appears or appears immediately. What you want is for the component to plug into the form system the same way does. Signal Forms, briefly Angular 22 made Signal Forms stable. A form is a signal holding your model, plus a schema: Two things to internalise: binds a control to a field. It is the replacement for . Calling a field node gives you its state : returns an object of signals — , , , , , , , , . Validation lives in the schema, in one place, typed against your model. Rename in the interface and the schema fails to compile. Building the custom control To make work with , implement and expose a model signal. That is the whole contract. Count what is not there: no provider, no , no , no , no , no . A and an interface. Using it detects that the component implements , wires two-way, and pushes , , , , and down as inputs. The component does not read the form; the form drives the component. For on/off controls there is a matching interface — implement with a instead. The valve toggle from Chapter 3 becomes form-compatible by adding one interface and renaming one signal. Display versus stored value When what the user types differs from what you store, use : Where still applies Signal Forms are the recommendation for new work. CVA has not gone anywhere: Situation Use --- --- New control, new form / Existing app on Reactive Forms — still fully supported A control that must work with both Implement both; they coexist A third-party CVA you do not want Signal Forms to adopt Add to opt it out That last row is a genuine Angular 22 feature and worth remembering: tells to ignore an existing on an element, which matters when you migrate a library gradually and a component implements both. Accessibility note The picker above uses elements with , which gives keyboard access for free. If the silos are genuinely a single-select list rather than a set of toggles, Chapter 3's advice applies: 's Listbox gives you roving focus, typeahead, and arrow keys, and you can implement on top of it. The two compose. "But I thought custom form controls were supposed to be hard" The reputation is earned. is genuinely awkward, and the difficulty was never really about the four methods — it was about what those methods force you to build. CVA is a push-based interface designed before signals existed. It hands you callbacks and expects you to store them, call them at the right moments, and keep your own internal copy of the value in step with the form's copy: Two sources of truth, kept in sync by convention. Every bug in custom controls came from that: forgetting so validation never fires, forgetting so the control stays live in a disabled form, calling inside and causing a loop. Plus the registration ritual — , , and because the class is not defined yet at decorator evaluation time. Nobody could explain on the spot, which told you something. removes the shadow copy. is the two-way binding, so there is one value and it lives in one place. The form reads and writes the same signal you render from. --- --- --- Interface methods to implement 4 0 — one Provider registration + + None Sources of truth for the value 2, synced by hand 1 Disabled state callback input Errors available to the control No — parent renders them input Typical lines for a simple control 45 12 Classic bug: forgot Common Not possible So the belief was accurate about the old API and is now out of date. If you learned custom controls the hard way, the useful update is: the difficulty was accidental, and Angular removed it. Cheat sheet Do Don't --- --- with Invent / with for on/off Use Accept , , , , as inputs Track them yourself Emit on blur Let blur-based validation silently break for display/storage mismatch Parse in the template Keep validation in the form schema Validate inside the control when migrating a control that has both Fight the interop Recap Signal Forms are stable in Angular 22. plus a schema, bound with . A custom control is and a . That is the entire contract. with for on/off controls. The form drives your state inputs — , , , . Do not track them. Emit on blur or blur-based v…