Repo of the Day
Khalzy/rhf-granular
Published: Feb 24, 2026
Open repository ↗Computed values and granular subscriptions for React Hook Form.
Summary
rhf-granular is a TypeScript library that adds computed values and granular subscriptions to react-hook-form. It addresses re-render issues caused by the standard watch and useWatch hooks, which can cause components to re-render on every form change even when the value they depend on hasn't changed. The library requires React 18+ and react-hook-form 7+.
What it is useful for
This library is useful for React applications using react-hook-form where re-render performance is a concern. It fills gaps that the built-in useWatch and compute option don't cover:
- Form-wide selectors: access
values,errors,isSubmitting,isValidatingand other form meta together in one selector. The built-incomputeoption only receives field values. - Field state without controlled inputs: read
isTouched,isDirty, orerrorfor a field without usinguseController. - Side effects without render cycles: trigger API calls or analytics when form values change, without the render-then-effect pattern.
- Derived values that only re-render when the result changes: calculations like
price = base * seatsthat would otherwise requireuseMemo+useWatchboilerplate.
How engineers can use it
Setup:
npm install rhf-granular
The library exposes three hooks:
useFormSelector— Subscribe to a derived value; the component only re-renders when the result changes.
const isEnterprise = useFormSelector(
control,
(s) => s.values.plan === "enterprise"
);
useFormValue— Subscribe to a single field by dot-notation path.
const plan = useFormValue(control, "plan");
const firstName = useFormValue(control, "users.0.firstName");
useFormEffect— Run side effects on form state changes without causing re-renders.
useFormEffect(control, ({ values, isDirty }) => {
if (values.plan === "enterprise" && isDirty) {
trackEvent("enterprise_plan_edited");
}
});
Documented limitations and patterns to avoid:
- Components render twice on mount (four times in StrictMode) due to standard RHF initialization. The README states this does not happen in production builds.
- Reading any property from
formStateat the top level subscribes the component to every form state change, causing the whole tree to re-render on every keystroke. useFormContextis not safe for reactive reads; it's only safe for pulling stable references likecontrol,register,handleSubmit, andsetValue.- Destructuring
fieldsfromuseFieldArrayre-renders on every array mutation. For metadata likecount, use a selector instead.
The README includes a detailed "footguns" section that should be read before integrating the library, as several common patterns will silently cause full re-renders.