Form validation often triggers unnecessary re-renders, especially in large React apps. In this guide, we’ll build a no-re-render validation system using React Context, Refs, and a touch of functional magic — perfect for complex forms where performance matters.

Why Avoid Re-Renders?


Each validation change (like user typing) can cause entire form sections to re-render, creating lag. By using refs and a pub-sub model via context, we keep validation outside of the render lifecycle entirely.

Step 1: Create a Validation Context


This will manage subscribers (inputs) without triggering renders.

// ValidationContext.js
import { createContext, useContext, useRef } from "react";

const ValidationContext = createContext();

export function ValidationProvider({ children }) {
const fields = useRef(new Map());

const register = (name, validateFn) => {
fields.current.set(name, validateFn);
};

const validateAll = () => {
let valid = true;
fields.current.forEach((validateFn) => {
if (!validateFn()) valid = false;
});
return valid;
};

return (

{children}

);
}

export const useValidation = () => useContext(ValidationContext);

Step 2: Register Inputs Without State


Each input registers its validator but doesn’t subscribe to global form state.

// ValidatedInput.js
import { useEffect, useRef } from "react";
import { useValidation } from "./ValidationContext";

export function ValidatedInput({ name, validate, ...props }) {
const inputRef = useRef();
const { register } = useValidation();

useEffect(() => {
register(name, () => validate(inputRef.current.value));
}, [name, validate, register]);

return ;
}

Step 3: Validate the Form on Submit


The form can validate all fields at once without state updates.

// Form.js
import { ValidationProvider, useValidation } from "./ValidationContext";
import { ValidatedInput } from "./ValidatedInput";

function Form() {
const { validateAll } = useValidation();

const handleSubmit = (e) => {
e.preventDefault();
if (validateAll()) {
alert("Form is valid!");
} else {
alert("Form has errors.");
}
};

return (


name="email"
validate={(v) => v.includes("@")}
placeholder="Email"
/>
name="password"
validate={(v) => v.length >= 6}
placeholder="Password"
type="password"
/>


);
}

export default function App() {
return (



);
}

How It Works


Instead of React state managing field errors, each input registers its validator with a central manager (context + ref map). The form then calls all validators at submit without re-rendering inputs during typing, leading to major performance wins for large forms.

Pros and Cons

✅ Pros


  • No render overhead for validation updates
  • Ultra-fast even with 100+ fields
  • Fully decoupled and testable validators

⚠️ Cons


  • Manual registration adds slight complexity
  • Not ideal for reactive, instant-feedback UIs without extra hooks
  • Browser-native validation still needs to be handled separately

🚀 Alternatives


  • react-hook-form: Extremely efficient form library with no re-render philosophy
  • Formik + FastField: Scoped optimizations in traditional form libraries
  • Zod validation: Use schema-based parsing instead of ad-hoc functions

Summary


If your forms feel sluggish or complicated, think outside the React state machine. Using refs and a simple validation context unlocks huge speed gains and scales beautifully for enterprise apps.

For a much more extensive guide on getting the most out of React portals, check out my full 24-page PDF file on Gumroad. It's available for just $10:

Using React Portals Like a Pro.

If this was useful, you can support me here: buymeacoffee.com/hexshift