A custom hook is just a regular function that calls other hooks — the convention of naming it useSomething is how React's linter recognizes it as a hook (and enforces the Rules of Hooks on it), not special syntax.
function useLocalStorage(key, initialValue) {
const [value, setValue] = useState(() => {
const stored = localStorage.getItem(key);
return stored ? JSON.parse(stored) : initialValue;
});
useEffect(() => {
localStorage.setItem(key, JSON.stringify(value));
}, [key, value]);
return [value, setValue];
}
function Settings() {
const [name, setName] = useLocalStorage("name", "");
return <input value={name} onChange={(e) => setName(e.target.value)} />;
}
This is where React's real reusability comes from — extracting stateful logic (not just UI) into a function that any component can call, exactly like useState and useEffect themselves.
useState call to its corresponding piece of state; conditionally skipping a hook call shifts every hook after it.Both rules exist for the same reason: React identifies each piece of hook state by call order, not by name — there's no "count" string tying useState(0) to a specific slot; it's purely "the 3rd hook call in this component."