Introduction
The main architecture mistake in React projects is rarely the choice of library. It is responsibility drift.
A component starts as a view. Then it fetches data. Then it transforms the response. Then it handles permissions. Then it owns validation. Then it writes analytics events. Then another feature imports it because it already has the behavior.
At some point, the component is no longer a component. It is a small application wearing JSX.
React is flexible enough to allow this. That flexibility is useful, but it also means teams need discipline.
Components Should Not Own Everything
React components are excellent at describing UI as a function of state. They are less excellent when they become the only place where product behavior can live.
A component can reasonably handle:
- Rendering.
- Local UI state.
- User events.
- Composition of child components.
But if the component also owns API transformation, business rules, feature permissions, formatting policy, and cross-page coordination, it becomes difficult to test and risky to change.
The issue is not file length by itself. A long component can be readable. A short component can be deeply coupled. The real smell is mixed reasons to change.
Business Logic Needs a Home
Every product has rules. Discounts expire. Users have roles. Forms depend on account status. Buttons are disabled for reasons that matter to the business.
If those rules are scattered across JSX, the product becomes hard to reason about.
Instead, give business logic a home:
function canInviteMember(user: User, workspace: Workspace) {
return user.role === "admin" && workspace.plan !== "free";
}
This tiny function is not impressive. That is why it is useful. It names a rule and makes it testable.
Senior architecture often looks like extracting boring names for important concepts.
Hooks Are Not a Dumping Ground
Custom hooks can improve architecture, but they can also hide mess.
Moving code from a component into useSomething does not automatically improve design. If the hook still fetches data, mutates global state, reads routing params, handles permissions, emits analytics, and formats UI labels, the responsibility problem remains.
A good hook should have a clear purpose:
- Manage a specific interaction.
- Connect to a specific external source.
- Compose related behavior behind a stable API.
If explaining the hook requires a paragraph of exceptions, it may be doing too much.
Shared Components Should Stay Generic
Another common mistake is pushing business behavior into shared UI components.
A shared table should know how to render rows, columns, sorting UI, empty states, and maybe selection. It should not know that invoices from enterprise accounts require a special warning unless that behavior is passed in from the feature.
When shared components absorb product-specific branches, every feature becomes coupled through a "reusable" component. Reuse then becomes a tax.
Good shared UI is boring and composable. Feature code should supply meaning.
Data Transformation Belongs Near Boundaries
APIs rarely return exactly what the UI wants. That does not mean every component should reshape responses inline.
Create clear boundaries:
- API layer receives external data.
- Transformation maps it into app-friendly models.
- Components render those models.
This keeps backend quirks from leaking everywhere.
It also makes future migrations easier. If the API changes, you update the boundary instead of hunting through JSX for response.data.attributes.something.
The Architecture Test
A useful test for React architecture is this:
If a product rule changes, where do you edit?
If the answer is "five components, two hooks, one shared helper, and a table column definition", the rule does not have a real home.
If the answer is "one named function and a few rendering consequences", the architecture is healthier.
This does not happen by accident. It comes from noticing concepts and giving them stable places in the codebase.
Conclusion
The main architecture mistake in React projects is mixing responsibilities until no one knows what owns what.
React will not stop you from doing this. That is why senior developers need to create boundaries intentionally: business rules outside JSX, data transformation near boundaries, shared components without product baggage, and hooks with clear purposes.
Good architecture does not make React more complicated. It lets React do what it does best: render UI from well-shaped state.