Components
React Components
Components are the building blocks of React applications. They let you split the UI into independent, reusable pieces.
What is a Component?
A component is a JavaScript function that returns JSX:
function Welcome() {
return <h1>Hello, React!</h1>;
}
That’s it! A component is just a function that returns what to display.
Function Components
The modern way to create components (React 16.8+):
function Greeting() {
return <h1>Hello!</h1>;
}
// Arrow function version
const Greeting = () => {
return <h1>Hello!</h1>;
};
// Implicit return (single expression)
const Greeting = () => <h1>Hello!</h1>;
Component Names
Must start with a capital letter:
// ✅ Right - capital letter
function MyComponent() { }
const UserCard = () => { };
// ❌ Wrong - lowercase (React treats as HTML tag)
function myComponent() { }
const userCard = () => { };
Using Components
Use components like HTML tags:
function Welcome() {
return <h1>Welcome!</h1>;
}
function App() {
return (
<div>
<Welcome />
<Welcome />
<Welcome />
</div>
);
}
Self-closing if no children:
<Welcome />
With children:
<Welcome>
<p>This is a child</p>
</Welcome>
Composing Components
Build complex UIs by nesting components:
function Header() {
return (
<header>
<Logo />
<Navigation />
</header>
);
}
function Logo() {
return <img src="/logo.png" alt="Logo" />;
}
function Navigation() {
return (
<nav>
<a href="/">Home</a>
<a href="/about">About</a>
</nav>
);
}
function App() {
return (
<div>
<Header />
<MainContent />
<Footer />
</div>
);
}
Component Organization
One component per file (recommended):
// components/Button.jsx
export default function Button() {
return <button>Click me</button>;
}
// App.jsx
import Button from './components/Button';
function App() {
return <Button />;
}
Multiple related components:
// components/UserProfile.jsx
function Avatar({ src }) {
return <img src={src} className="avatar" />;
}
function Bio({ text }) {
return <p className="bio">{text}</p>;
}
export default function UserProfile({ user }) {
return (
<div className="profile">
<Avatar src={user.avatar} />
<h2>{user.name}</h2>
<Bio text={user.bio} />
</div>
);
}
Extracting Components
When to extract a component:
1. Reusability:
// Before
function UserList() {
return (
<div>
<div className="user-card">
<img src="/user1.jpg" />
<h3>Alice</h3>
</div>
<div className="user-card">
<img src="/user2.jpg" />
<h3>Bob</h3>
</div>
</div>
);
}
// After - reusable component
function UserCard({ image, name }) {
return (
<div className="user-card">
<img src={image} />
<h3>{name}</h3>
</div>
);
}
function UserList() {
return (
<div>
<UserCard image="/user1.jpg" name="Alice" />
<UserCard image="/user2.jpg" name="Bob" />
</div>
);
}
2. Complexity:
// Before - too complex
function Dashboard() {
return (
<div>
<div className="header">
<img src="/logo.png" />
<nav>{/* 20 lines of navigation */}</nav>
<div>{/* User menu code */}</div>
</div>
<div className="sidebar">{/* Sidebar code */}</div>
<main>{/* Main content */}</main>
</div>
);
}
// After - extracted for clarity
function Dashboard() {
return (
<div>
<DashboardHeader />
<Sidebar />
<MainContent />
</div>
);
}
Pure Components
Components should be pure functions - same props → same output:
// ✅ Pure - predictable
function Greeting({ name }) {
return <h1>Hello, {name}!</h1>;
}
// ❌ Impure - uses external variable
let counter = 0;
function Counter() {
counter++; // Side effect!
return <div>{counter}</div>;
}
// ❌ Impure - modifies prop
function User({ user }) {
user.visited = true; // Mutation!
return <div>{user.name}</div>;
}
Component Patterns
1. Container/Presentation Pattern:
// Presentation - just displays data
function UserCard({ name, email, avatar }) {
return (
<div className="user-card">
<img src={avatar} alt={name} />
<h3>{name}</h3>
<p>{email}</p>
</div>
);
}
// Container - fetches/manages data
function UserCardContainer({ userId }) {
const [user, setUser] = useState(null);
useEffect(() => {
fetchUser(userId).then(setUser);
}, [userId]);
if (!user) return <div>Loading...</div>;
return <UserCard {...user} />;
}
2. Compound Components:
function Card({ children }) {
return <div className="card">{children}</div>;
}
Card.Header = function CardHeader({ children }) {
return <div className="card-header">{children}</div>;
};
Card.Body = function CardBody({ children }) {
return <div className="card-body">{children}</div>;
};
// Usage
<Card>
<Card.Header>
<h3>Title</h3>
</Card.Header>
<Card.Body>
<p>Content</p>
</Card.Body>
</Card>
3. Render Props Pattern:
function DataProvider({ url, render }) {
const [data, setData] = useState(null);
useEffect(() => {
fetch(url)
.then(r => r.json())
.then(setData);
}, [url]);
return render(data);
}
// Usage
<DataProvider
url="/api/users"
render={data => data ? <UserList users={data} /> : <Loading />}
/>
Component Lifecycle (Modern)
With hooks, components have three main phases:
1. Mount (component appears):
function Component() {
useEffect(() => {
console.log('Component mounted');
}, []); // Empty array = run once on mount
return <div>Hello</div>;
}
2. Update (props/state change):
function Component({ prop }) {
useEffect(() => {
console.log('Component updated');
}, [prop]); // Runs when prop changes
return <div>{prop}</div>;
}
3. Unmount (component removed):
function Component() {
useEffect(() => {
return () => {
console.log('Component unmounted');
};
}, []);
return <div>Hello</div>;
}
Component Communication
Parent to Child (Props):
function Parent() {
const message = "Hello from parent";
return <Child message={message} />;
}
function Child({ message }) {
return <div>{message}</div>;
}
Child to Parent (Callback):
function Parent() {
const handleClick = (data) => {
console.log('Child clicked:', data);
};
return <Child onAction={handleClick} />;
}
function Child({ onAction }) {
return (
<button onClick={() => onAction('some data')}>
Click me
</button>
);
}
Sibling Communication (Lift State Up):
function Parent() {
const [shared, setShared] = useState('');
return (
<>
<ChildA value={shared} onChange={setShared} />
<ChildB value={shared} />
</>
);
}
Best Practices
1. Keep components small and focused:
// Good - single responsibility
function Avatar({ src, alt }) {
return <img src={src} alt={alt} className="avatar" />;
}
// Bad - too many responsibilities
function UserSection({ user, posts, comments }) {
// Hundreds of lines...
}
2. Use descriptive names:
// Good
function UserProfileHeader() { }
function ProductListItem() { }
// Bad
function Component1() { }
function Thing() { }
3. Avoid deep nesting:
// Bad
<Parent>
<Child1>
<Child2>
<Child3>
<Child4>
// ...
</Child4>
</Child3>
</Child2>
</Child1>
</Parent>
// Good - flatten when possible
<Parent>
<Section1 />
<Section2 />
<Section3 />
</Parent>
4. Extract repeated JSX:
// Before
<div>
<button onClick={handleSave}>Save</button>
<button onClick={handleCancel}>Cancel</button>
<button onClick={handleDelete}>Delete</button>
</div>
// After
function ActionButton({ onClick, children }) {
return <button onClick={onClick}>{children}</button>;
}
<div>
<ActionButton onClick={handleSave}>Save</ActionButton>
<ActionButton onClick={handleCancel}>Cancel</ActionButton>
<ActionButton onClick={handleDelete}>Delete</ActionButton>
</div>
Practical Example: Blog Post
function BlogPost({ post }) {
return (
<article className="blog-post">
<PostHeader
title={post.title}
author={post.author}
date={post.date}
/>
<PostContent content={post.content} />
<PostFooter
likes={post.likes}
comments={post.comments}
/>
</article>
);
}
function PostHeader({ title, author, date }) {
return (
<header>
<h1>{title}</h1>
<AuthorInfo author={author} date={date} />
</header>
);
}
function AuthorInfo({ author, date }) {
return (
<div className="author-info">
<Avatar src={author.avatar} />
<span>{author.name}</span>
<time>{formatDate(date)}</time>
</div>
);
}
function Avatar({ src }) {
return <img src={src} className="avatar" alt="Avatar" />;
}
function PostContent({ content }) {
return <div className="content">{content}</div>;
}
function PostFooter({ likes, comments }) {
return (
<footer>
<LikeButton count={likes} />
<CommentCount count={comments} />
</footer>
);
}
Common Mistakes
1. Not starting with capital letter:
// Wrong
const myComponent = () => <div>Hello</div>;
<myComponent /> // Treated as HTML tag!
// Right
const MyComponent = () => <div>Hello</div>;
<MyComponent />
2. Returning multiple elements without wrapper:
// Wrong
function Component() {
return (
<h1>Title</h1>
<p>Text</p>
);
}
// Right
function Component() {
return (
<>
<h1>Title</h1>
<p>Text</p>
</>
);
}
3. Modifying props:
// Wrong
function Component({ user }) {
user.name = "Modified"; // Never mutate props!
return <div>{user.name}</div>;
}
// Right
function Component({ user }) {
const displayName = user.name.toUpperCase();
return <div>{displayName}</div>;
}
Next Article: Props