Skip to content

When Components Get Too Fat

When a component starts getting "fat," it’s usually not because the file size is huge, but because the list of props has turned into a grocery list.

2 min read
React Composition
Render Props
Custom Hooks
Compound Components
Frontend Architecture

When a component starts getting "fat," it’s usually not because the file size is huge, but because the list of props has turned into a grocery list. Every new feature adds another line, and before you know it, the component is a nightmare to manage.

Back in the day, if we wanted to share logic without the headache of Higher-Order Components (HOCs), we used render props. The concept was straightforward: the parent handles the logic, and the child handles the UI. But the fatal flaw was the nesting. Once you start nesting functions deeper and deeper—especially with TypeScript—managing those types becomes a total nightmare.

Hooks arrived as a much cleaner solution. They let us extract logic into independent, testable units without wrapping components in endless layers. That said, I’m not the type to go through an old codebase and delete every render prop just for the sake of being "ideal." I only migrate when the usage becomes unclear or the structure is too messy to test.

I usually look for two signs that it’s time to switch to hooks: 1. When a render prop is essentially just a "pipe" passing state that a hook could easily provide. 2. When I see three or more nested render props.

But it’s not a one-size-fits-all situation. Render props and slots still make more sense in certain scenarios—like when a library needs to render elements within a layout it doesn't control, or when you need to inject an element per item, like a renderItem function in a list. A hook can't "draw" an element into a specific row of a table managed by another component; that's where slots or render props are still crucial.

If the issue is actually a complex component structure, I'd suggest compound components instead. Rather than building one giant component with dozens of props to control every tiny detail, it's better to break it down into smaller, cooperating parts.

In practice, I avoid massive refactors in a single pull request just for the sake of aesthetics. A safer strategy is to extract the logic into a hook first, let the old component act as a thin wrapper, and then phase it out once all its callers have migrated.

The goal is simple: keep components slim and make sure the next person reading the code isn't totally lost.