Skip to Content
DocsState Subscriptions

Subscriptions & Watchers

Formly is built on a high-performance Pub-Sub event bus architecture. Rather than forcing the entire React form component to re-render on every keystroke, input values are updated directly inside their respective target elements.

To sync state across fields or trigger React layout updates, Formly provides two subscription mechanisms: watch and subscribe.


Reactive Watchers: form.watch

The form.watch() method registers a reactive subscription. Whenever the watched field path changes, it forces the calling React component to re-render.

This is the standard approach for conditionally rendering sections or updating text displays based on user selections.

Example: Conditional Field Rendering

import React from "react"; import { Form, Field, useForm } from "@explita/formly"; export default function CheckoutForm() { const form = useForm({ defaultValues: { hasGiftMessage: false, giftMessage: "", } }); // Watch the checkbox field const showMessage = form.watch("hasGiftMessage"); return ( <Form use={form}> <Field name="hasGiftMessage" type="checkbox" label="Include a free gift message?" render={(props) => <input {...props} type="checkbox" />} /> {/* Conditionally render message input */} {showMessage && ( <div className="gift-message-section" style={{ marginTop: "1rem" }}> <Field name="giftMessage" label="Gift Message Text" render={(props) => <textarea {...props} maxLength={200} />} /> </div> )} <button type="submit" style={{ marginTop: "1.5rem" }}> Place Order </button> </Form> ); }

Performance Listeners: form.subscribe

For performance-critical code or actions that don’t need visual layout changes, form.subscribe() registers an event listener. It triggers a callback whenever the target field value changes without forcing a React re-render.

This is ideal for logging, tracking analytics, updating background caches, or triggering secondary mutations.

Example: Background Logging

import React, { useEffect } from "react"; import { useForm } from "@explita/formly"; function AnalyticLogger() { const form = useForm({ defaultValues: { email: "" } }); useEffect(() => { // Subscribe directly to the email channel const unsubscribe = form.subscribe("email", (newEmail) => { // Runs on every keystroke, but AnalyticLogger does NOT re-render! console.log("Analytics: email input changed to:", newEmail); }); // Cleanup subscription on unmount return unsubscribe; }, [form]); return ( <form.Field name="email" label="Email" render={(props) => <input {...props} />} /> ); }

Summary comparison

Use this table to choose the correct approach:

Use CaseMethodReact Re-render?
Conditionally hiding/showing form sectionsform.watch("path")Yes (Triggered on value change)
Displaying calculated totals in text nodesform.watch("path")Yes (Triggered on value change)
Triggering analytical trackers (Google Analytics)form.subscribe("path", cb)No
Saving draft states silently to a remote DBform.subscribe("path", cb)No
Writing logs or syncing with non-React storesform.subscribe("path", cb)No
Last updated on