Directives vs Custom Hooks: The Reuse Primitive Some logic isn't really about what a component renders — it's about behavior attached to an element. Detecting a click outside a dropdown to close it. Tracking whether an element is hovered. Trapping focus inside a modal. Auto-resizing a textarea as the user types. None of that is markup. All of it needs to attach to a DOM element, do something reactive, and clean up after itself — and every component that needs it faces the same choice: copy the logic in, or extract it once and reuse it everywhere. The reuse primitive for exactly this kind of cross-cutting, element-attached behavior is a directive in Angular and a custom hook in React. Neither is a component. That's the point — this logic doesn't own any rendering, so it shouldn't be forced into something that does. Why this isn't a component's job A component's job is to decide what appears on screen. Click-outside detection, hover tracking, and focus trapping don't decide what appears — they decide when something else should happen in response to the DOM. Wrapping that logic in a component that renders nothing ( ) works, but forces callers to accept an extra layer in the tree just to attach behavior to their own element. A directive or hook attaches the behavior directly, with no wrapper and no extra DOM node. In Angular An attribute directive attaches behavior to any host element without owning its template at all: In React A custom hook does the identical job — attach behavior to a ref, react to events, clean up on unmount — without wrapping anything: Both versions do the same job: decorate an existing element with reusable behavior, with zero impact on what actually renders. The framework changes the syntax, not the decision. Directive/hook, or service/store? Not all reusable logic belongs at this level. The dividing line is whether the logic is tied to a specific DOM element or genuinely app-wide: Tied to one element's lifecycle — click-outside, hover, resize-observation, focus trap, drag handles. This is directive/hook territory. It lives and dies with the component that uses it. Shared, app-wide state — the current user, a feature flag, cached server data. This belongs in a service (Angular) or a higher-level hook backed by context/a store (React) — covered in the state management chapter, not here. Getting this wrong in one direction means duplicating DOM-event boilerplate in five components. Getting it wrong in the other means smuggling app-wide state into something that's supposed to be a disposable, element-scoped utility. The Trade-off: Is Extracting a Directive or Hook Worth It? For a single component, inlining the click-outside logic directly is less code than writing a separate directive or hook and wiring it up. The extraction only pays off once the logic needs to live in more than one place. The pain, concretely Copy-paste behavior degrades quietly, because each copy looks fine in isolation: v1: needs to close on an outside click. You write the listener inline, in a — five lines, done, works. v2: A context menu needs the same behavior. Fastest path: copy the five lines from . Still fine — it's "just five lines." v3: A modal needs it too, but whoever copies it this time forgets the cleanup — no on unmount. Nothing breaks in dev. In production, under heavy navigation, listeners accumulate and the page slowly leaks memory, and the bug report has no obvious connection to "the modal's click-outside code." v4: The team decides the detection should also ignore clicks on a specific "ignore" class (to support a tooltip inside the dropdown). That fix has to be found and reapplied in all three copies — and by now nobody's sure a fourth copy doesn't exist somewhere else in the codebase. The bug in v3 isn't a sign someone wrote careless code. It's what happens when the same logic is retyped by hand more than once — eventually, one copy will diverge. Side-by-side Copy-pasted into each component Shared directive / hook --- --- --- Fixing a bug in the detection logic Must find and patch every copy Fix once, every consumer gets it Risk of a missed cleanup (memory leak) High — each copy re-implements teardown by hand None — written correctly once Adding a new component that needs it Copy five-plus lines again One line: apply the directive / call the hook Upfront cost None for the first use A few minutes to extract into its own file The signal to switch The moment the same event-listener-plus-cleanup logic is about to be written in a second component, that's the trigger. One use is a fine inline or lifecycle hook. A second use is a duplicate waiting to drift. Start simple For a behavior that's only ever needed in one component, write it inline — extracting a directive or hook for a single caller adds a file for no present benefit. Extract on the second real use, not in anticipation of one that hasn't happened yet. The rule Cross-cutting behavior that attaches to a specific element belongs in a directive (Angul…