Skip to content
albarmox logo
albarmox
software engineer · frontend
Back to BlogsJune 22, 2026

Building Reusable React Components: The Right Way

Learn how to design components with single responsibility, proper typing, and clear interfaces — and why separating UI from business logic matters.

Building Reusable React Components: The Right Way

One of the most common mistakes in React development is mixing business logic with UI rendering inside the same component. This might seem harmless at first — the component works, after all — but it creates a tightly coupled, untestable, and unreusable piece of code. ## The Single Responsibility Principle Every component should have one job. A Button renders a button and handles click events. A UserCard renders user information. A UserTable renders a list of users. None of them should be making API calls directly. **Bad:** ```tsx export function Button() { const submitUser = async () => { await fetch("/api/users"); }; return <button onClick={submitUser}>Submit</button>; } ``` This Button is now permanently tied to the user API. You can't reuse it for anything else. **Good:** ```tsx type ButtonProps = { children: React.ReactNode; onClick?: () => void; disabled?: boolean; type?: "button" | "submit" | "reset"; }; export function Button({ children, onClick, disabled = false, type = "button" }: ButtonProps) { return ( <button type={type} onClick={onClick} disabled={disabled}> {children} </button> ); } ``` This Button works anywhere in your application. ## Size Guidelines Components should stay manageable: - **Small**: 20–80 lines — simple, pure UI - **Medium**: 80–150 lines — composed UI with some state - **Large**: up to ~200 lines — complex sections If a component exceeds 200 lines, it's a signal to break it apart. A UserPage that renders a filter, table, and modal should be decomposed into UserFilter, UserTable, and UserFormModal — each in its own file. ## Props Design Props are the public API of your component. Design them intentionally: - Use typed interfaces, never raw `any` - Name boolean props naturally: `isLoading`, `isDisabled`, `hasError` — not `loading`, `disabled`, or `flag` - If props exceed ~6 fields, consider passing a single object instead of individual values ```tsx // Hard to read <UserForm id={id} name={name} email={email} phone={phone} role={role} status={status} /> // Better <UserForm user={user} /> ``` ## Where Components Live The project structure should reflect component responsibility: - `components/ui/` — Base UI components (Button, Input, Badge). No business logic. - `components/layout/` — Layout shells (Navbar, Sidebar, Footer). - `components/shared/` — Reusable cross-feature components (StatusBadge, ConfirmDialog). - `features/feature-name/components/` — Feature-specific components that aren't shared. This separation makes it immediately clear what a component does, who owns it, and whether it can be reused.

Localoka project preview