Join our Newsletter — 33% off our NHI Course

What is the difference between dead code and undead code in React components?

Dead code is a method or block that is not used anywhere and can be removed safely once you confirm there are no references. Undead code looks unused inside one component, but it is still invoked from elsewhere, often through props, callbacks, or external method access. The distinction matters because undead code can look disposable while still being part of the component contract.

How Dead Code Differs from Undead Code in React Components

dead code and undead code look similar on the surface, but they fail in different ways. Dead code has no active call path and can usually be removed after confirming there are no references. Undead code is still part of the component contract, even if it appears unused inside that file, so deleting it can break props, callbacks, or external callers.

In React, that distinction is important because local readability can be misleading. A function may look dormant in the component body, yet still be invoked by a parent, passed as an event handler, or reached through a ref or other external access pattern. The question is not whether the code is visibly used in one file, but whether the component still exposes it as behavior.

Why React Makes the Difference Easy to Miss

React encourages composition, indirection, and callback-driven behavior, so usage is often distributed across files rather than obvious inside the component itself. A handler can be passed down several levels, wrapped in memoization, or triggered by a child component without any direct local call site. That makes “looks unused” a weak signal on its own.

Dead code is usually a codebase hygiene problem, while undead code is a contract problem. Removing dead code reduces clutter, but removing undead code can change runtime behavior, break integration points, or silently disable a feature path that another component still depends on.

This is especially common with helper methods, event handlers, custom hooks, and legacy props that still exist for compatibility. If the component is part of a public or semi-public interface, the safest assumption is that apparent inactivity inside one component does not prove irrelevance overall.

How to Decide Whether Code Is Safe to Remove

Start by tracing who can call it, not just where it is called locally. Check parent props, child callbacks, refs, exported helpers, tests that encode expected behavior, and any non-React usage such as imperative method access or framework integrations. If the path is external to the file, treat the code as live until proven otherwise.

A practical rule is to delete only when you can demonstrate both of these conditions: no reachable call path exists, and no contractual expectation depends on the code remaining present. If either condition is unclear, the code is probably undead rather than dead.

For larger component trees, the safest cleanup pattern is to remove usage first, then remove the implementation after a full search confirms there are no remaining consumers. That avoids breaking behavior that was hidden by abstraction or indirection.

Practitioner Guidance

What to verify: Before deleting a suspicious method or block, search for prop passing, callback wiring, ref access, tests, and any exported or indirectly invoked usage. In React, the absence of a local call site is not enough to prove dead code.

Common mistake: Teams often remove code based on one component file alone, then discover a parent still relies on the callback or method signature. The visible component body is only part of the usage graph.

Decision rule: If the code is not referenced anywhere and no external contract depends on it, treat it as dead code and remove it. If another component, consumer, or integration can still reach it, treat it as undead code and preserve or refactor the interface first.

Practitioner takeaway: Dead code is unused implementation, but undead code is still behavior exposed by the component contract. In React, safe cleanup depends on tracing reachability across components, not on what looks idle inside one file.