It's usually a code smell to break one of the rules of React hooks, but sometimes it's necessary.
I had a good reason to call hooks in a loop while building the keyboard shortcut functionality for this website. The keyboard shortcuts are defined in a configuration file, and I decided to use react-hotkeys-hook to manage the keyboard shortcuts, which supports adding individual shortcuts via the useHotkeys hook.
I wanted to avoid hard-coding the indices of the shortcuts in the configuration file, so I decided to use a loop to call the useHotkeys hook for each shortcut. Then I remembered the rule...
Don't call Hooks inside loops, conditions, or nested functions. Instead, always use Hooks at the top level of your React function, before any early returns. -Rules of Hooks
In comes utility components.


The rule is pretty clear - "always use Hooks at the top level of your React function." So let's just execute our hooks in a component that renders nothing, so the hook is called at the top level.
const KeyboardShortcuts = () => {
	return (
			{{ keys, callback }) => (
				<UseHotkey key={index} keys={keys} callback={callback} />

const UseHotkey: React.FC<{
	keys: Keys;
	callback: HotkeyCallback;
	options?: Options;
	deps?: unknown[];
}> = ({ keys, callback, options, deps }) => {
	useHotkeys(keys, callback, options, deps);
	return null;
Boom, now we can call the useHotkeys hook in a loop, and take advantage of the benefits of centrally-located app configuration. With this structure, we can pypass the constraint against invoking hooks within loops but abstracting each hook call into its dedicated component instance.