React Component Patterns You Should Know (But Probably Don’t)
When making React app, most people just write components in random way. They put all logic, state, and UI in one file, and later, when app gets big, everything becomes mess. But there are better ways to do it. React has some patterns that help keep code clean, easy to read, and not so hard to fix when something goes wrong.
Here are some patterns you maybe never heard about but should know.
1. Container and Presentational Components
Most people mix logic and UI together in same component. This makes it hard to reuse and test. Instead, better way is to split them.
Example:
// Container Component (handles logic)
function UserContainer() {
const [user, setUser] = React.useState(null);
React.useEffect(() => {
fetch("https://api.example.com/user")
.then(res => res.json())
.then(data => setUser(data));
}, []);
return <UserProfile user={user} />;
}
// Presentational Component (only UI)
function UserProfile({ user }) {
if (!user) return <p>Loading...</p>;
return <h1>Welcome, {user.name}!</h1>;
}
Now if you want different UI but same logic, you just change UserProfile. No need to rewrite everything.
2. Compound Components
Instead of passing too many props, this pattern lets components work together in smart way. Good example is <select> and <option> in HTML – they work together but still separate.
Example:
function Tabs({ children }) {
const [active, setActive] = React.useState(0);
return React.Children.map(children, (child, index) =>
React.cloneElement(child, { active, index, setActive })
);
}
function Tab({ index, active, setActive, children }) {
return (
<button
style={{ fontWeight: active === index ? "bold" : "normal" }}
onClick={() => setActive(index)}
>
{children}
</button>
);
}
function TabPanel({ index, active, children }) {
return active === index ? <div>{children}</div> : null;
}
// Usage
<Tabs>
<Tab>Tab 1</Tab>
<Tab>Tab 2</Tab>
<TabPanel>Content for Tab 1</TabPanel>
<TabPanel>Content for Tab 2</TabPanel>
</Tabs>
Now Tab and TabPanel know what to do without passing too many props.
3. Render Props
Instead of making a component do one thing, render props lets it be more flexible.
Example:
function MouseTracker({ render }) {
const [position, setPosition] = React.useState({ x: 0, y: 0 });
React.useEffect(() => {
const handleMove = (e) => setPosition({ x: e.clientX, y: e.clientY });
window.addEventListener("mousemove", handleMove);
return () => window.removeEventListener("mousemove", handleMove);
}, []);
return render(position);
}
// Usage
<MouseTracker render={({ x, y }) => <p>Mouse at {x}, {y}</p>} />
Now, you can use MouseTracker in different ways without changing the main component.
Recommended by LinkedIn
4. Higher-Order Components (HOC)
HOCs are functions that take a component and return a new one with extra features. Useful for things like authentication or logging.
Example:
function withAuth(Component) {
return function AuthenticatedComponent(props) {
const user = localStorage.getItem("user");
if (!user) return <p>Please log in</p>;
return <Component {...props} user={JSON.parse(user)} />;
};
}
// Usage
const Dashboard = ({ user }) => <h1>Welcome {user.name}!</h1>;
const ProtectedDashboard = withAuth(Dashboard);
Instead of adding login checks everywhere, just wrap component in withAuth.
5. Controlled vs Uncontrolled Components
When dealing with forms, people often forget difference between controlled and uncontrolled components.
Example of Controlled:
function ControlledInput() {
const [value, setValue] = React.useState("");
return <input value={value} onChange={(e) => setValue(e.target.value)} />;
}
Example of Uncontrolled:
function UncontrolledInput() {
const inputRef = React.useRef();
function handleSubmit() {
alert(inputRef.current.value);
}
return (
<div>
<input ref={inputRef} />
<button onClick={handleSubmit}>Submit</button>
</div>
);
}
Use controlled components when you need React to manage state, and use uncontrolled when you don’t need to re-render on every change.
Final Thoughts
If you only write React components in one way, your app will be hard to manage later. These patterns make code easier to read, reuse, and scale. You don’t need to use all of them, but knowing them will help you become better at React.
Try using Container & Presentational Components to separate logic from UI, Compound Components to make components work together smartly, and Render Props or HOCs to add reusable behavior.
Which pattern do you already use, and which one do you want to try next?
Great article to follow best practice.
Useful tips