Introduction
React is built on the philosophy that user interfaces can be broken down into small, independent, and reusable pieces called components. This idea forms the foundation of modern React development. Instead of thinking of an application as a collection of pages, React encourages developers to think in terms of components and how these components interact with each other.
Another essential concept closely tied to components is composition. Composition allows developers to build complex user interfaces by combining smaller, simpler components. By composing components together, developers create scalable applications that are easier to manage, extend, and maintain.
This post provides a detailed introduction to React components and composition, their importance, how they work, and how to use them effectively in real-world applications.
What Are React Components?
A React component is a JavaScript function or class that returns a piece of UI. Every React application is essentially a tree of components that together form the user interface.
There are two main types of components in React:
- Functional Components
- Class Components
Functional Components
Functional components are simply JavaScript functions that return JSX. They are lightweight, easy to write, and commonly used in modern React development.
function Greeting() {
return <h1>Hello, welcome to React!</h1>;
}
export default Greeting;
In this example, the Greeting component is a function that returns an h1 element.
Class Components
Before React Hooks, class components were the standard way to manage state and lifecycle methods. They are defined using ES6 classes and extend React.Component.
import React, { Component } from 'react';
class Greeting extends Component {
render() {
return <h1>Hello, welcome to React!</h1>;
}
}
export default Greeting;
While class components are still supported, most modern React codebases prefer functional components with hooks for simplicity and readability.
Why Components Matter
The concept of components is what makes React powerful. Components bring the following benefits:
- Reusability: Once created, a component can be reused throughout the application.
- Isolation: Components manage their own logic and styles, reducing interference with other parts of the application.
- Maintainability: Smaller, modular pieces are easier to maintain and debug.
- Scalability: Large applications can be broken into manageable parts.
For example, a button component written once can be reused in multiple places with different labels or styles.
function Button({ label }) {
return <button>{label}</button>;
}
// Reuse
<Button label="Login" />
<Button label="Signup" />
<Button label="Submit" />
Introduction to Composition in React
Composition is the process of combining smaller components to build larger, more complex UIs. It is one of the core patterns in React. Instead of relying on inheritance, React emphasizes composition as a way to share logic and structure between components.
For example, let’s create a simple Card component that accepts children and other props:
function Card({ title, children }) {
return (
<div className="card">
<h2>{title}</h2>
<div>{children}</div>
</div>
);
}
// Using composition
<Card title="Profile">
<p>Name: John Doe</p>
<p>Email: [email protected]</p>
</Card>
Here, the Card component can be used with any children passed to it, making it reusable and flexible.
Thinking in Components
When building an application, React encourages developers to break down the UI into a component hierarchy. For example, consider a simple blog page with a header, post list, and footer.
Example Hierarchy
- App
- Header
- PostList
- PostItem
- Footer
Each part of the UI becomes its own component.
Example Code
function Header() {
return <header><h1>My Blog</h1></header>;
}
function PostItem({ title, content }) {
return (
<article>
<h2>{title}</h2>
<p>{content}</p>
</article>
);
}
function PostList() {
const posts = [
{ id: 1, title: "First Post", content: "This is the first post content." },
{ id: 2, title: "Second Post", content: "This is the second post content." }
];
return (
<div>
{posts.map(post => (
<PostItem key={post.id} title={post.title} content={post.content} />
))}
</div>
);
}
function Footer() {
return <footer><p>© 2025 My Blog</p></footer>;
}
function App() {
return (
<div>
<Header />
<PostList />
<Footer />
</div>
);
}
export default App;
This example demonstrates how components can be composed together to build a complete application structure.
Props and Composition
Props (short for properties) are the way data is passed from parent to child components. They enable composition by allowing parent components to configure and customize child components.
function Button({ label, onClick }) {
return <button onClick={onClick}>{label}</button>;
}
function App() {
return (
<div>
<Button label="Save" onClick={() => alert("Saved!")} />
<Button label="Cancel" onClick={() => alert("Cancelled!")} />
</div>
);
}
Here, the same Button component is reused with different props to perform different actions.
Children Prop in Composition
The children prop allows components to accept and render nested elements, making composition powerful.
function Layout({ children }) {
return (
<div className="layout">
<header>Header Area</header>
<main>{children}</main>
<footer>Footer Area</footer>
</div>
);
}
function App() {
return (
<Layout>
<h1>Welcome to My App</h1>
<p>This content is injected into the layout.</p>
</Layout>
);
}
This pattern is widely used to build layouts, modals, and reusable containers.
Composition vs Inheritance
React recommends composition over inheritance for code reuse.
Inheritance Approach (Not Recommended)
class FancyButton extends React.Component {
render() {
return <button className="fancy">{this.props.label}</button>;
}
}
Composition Approach (Recommended)
function Button({ children }) {
return <button className="fancy">{children}</button>;
}
// Usage
<Button>Click Me</Button>
The composition approach is simpler, more flexible, and aligns with React’s design philosophy.
Container and Presentational Components
A common pattern in React composition is to separate logic-heavy components (containers) from UI-focused components (presentational).
Example
function UserList({ users }) {
return (
<ul>
{users.map(user => <li key={user.id}>{user.name}</li>)}
</ul>
);
}
function UserContainer() {
const users = [
{ id: 1, name: "Alice" },
{ id: 2, name: "Bob" }
];
return <UserList users={users} />;
}
Here, UserContainer manages data, while UserList handles rendering.
Real-World Example: Building a Dashboard with Composition
Imagine building a dashboard with widgets like statistics, charts, and user info. Using composition, you can create reusable widgets and combine them.
function Widget({ title, children }) {
return (
<div className="widget">
<h3>{title}</h3>
<div>{children}</div>
</div>
);
}
function Dashboard() {
return (
<div>
<Widget title="User Stats">
<p>Active Users: 120</p>
</Widget>
<Widget title="Revenue">
<p>$4500 this month</p>
</Widget>
<Widget title="Notifications">
<ul>
<li>New signup: John</li>
<li>Server update scheduled</li>
</ul>
</Widget>
</div>
);
}
This approach makes it easy to add, remove, or modify widgets without rewriting large parts of the UI.
Best Practices for Components and Composition
- Keep components small and focused.
- Use props effectively to pass data.
- Use children for flexible composition.
- Favor composition over inheritance.
- Break down complex UIs into hierarchies.
- Name components clearly to reflect their purpose.
- Reuse components to avoid duplication.
- Use keys properly when rendering lists.
Leave a Reply