Learn how to create reusable, modular functional components that form the building blocks of React applications.
Components are the heart of React. They let you split the UI into independent, reusable pieces, and think about each piece in isolation. Functional components are the modern, preferred way to create components in React.
Think of components as custom HTML elements that you can create and reuse throughout your application. Just like you use <div>, <button>, or <input> elements, you can create your own elements like <UserProfile>, <ShoppingCart>, or <WeatherWidget>.
Before React, we built UIs like this:
<!-- Repetitive HTML -->
<div class="user-card">
<img src="user1.jpg" alt="John Doe">
<h3>John Doe</h3>
<p>Software Engineer</p>
<button>Follow</button>
</div>
<div class="user-card">
<img src="user2.jpg" alt="Jane Smith">
<h3>Jane Smith</h3>
<p>UX Designer</p>
<button>Follow</button>
</div>
<!-- More repetitive cards... -->
With React components, we do this:
// Define once
function UserCard({ user }) {
return (
<div className="user-card">
<img src={user.avatar} alt={user.name}>
<h3>{user.name}</h3>
<p>{user.role}</p>
<button>Follow</button>
</div>
);
}
// Use everywhere
<UserCard user={john} />
<UserCard user={jane} />
<UserCard user={mike} />
A functional component is simply a JavaScript function that returns JSX:
// Method 1: Function Declaration
function Welcome() {
return <h1>Hello, React!</h1>;
}
// Method 2: Arrow Function
const Welcome = () => {
return <h1>Hello, React!</h1>;
}
// Method 3: Arrow Function (implicit return)
const Welcome = () => <h1>Hello, React!</h1>;
❌ Wrong:
function welcome() { // lowercase name
return <h1>Hello</h1>;
}
function Welcome() {
// No return statement
}
✅ Correct:
function Welcome() {
return <h1>Hello</h1>;
}
Let's build some practical components step by step.
function Greeting() {
return (
<div className="greeting">
<h1>Welcome to React!</h1>
<p>Let's build amazing things together.</p>
</div>
);
}
// Usage
function App() {
return (
<div>
<Greeting />
<Greeting />
<Greeting />
</div>
);
}
function UserProfile() {
// Component can have its own logic
const user = {
name: "Sarah Johnson",
role: "Frontend Developer",
avatar: "sarah.jpg",
location: "San Francisco, CA",
isOnline: true
};
return (
<div className="user-profile">
<div className="avatar-container">
<img
src={user.avatar}
alt={user.name}
className="avatar"
/>
{user.isOnline && (
<div className="online-indicator"></div>
)}
</div>
<div className="user-info">
<h2>{user.name}</h2>
<p className="role">{user.role}</p>
<p className="location">{user.location}</p>
</div>
<div className="actions">
<button className="btn-primary">Connect</button>
<button className="btn-secondary">Message</button>
</div>
</div>
);
}
function ProductCard() {
const product = {
id: 1,
name: "Wireless Headphones",
price: 99.99,
image: "headphones.jpg",
rating: 4.5,
inStock: true,
features: ["Noise Cancelling", "30hr Battery", "Wireless"]
};
const formatPrice = (price) => `$\${price.toFixed(2)}`;
const renderStars = (rating) => {
return Array.from({ length: 5 }, (_, index) => (
<span
key={index}
className={index < rating ? "star filled" : "star"}
>
★
</span>
));
};
return (
<div className="product-card">
<div className="product-image">
<img src={product.image} alt={product.name} />
{!product.inStock && (
<div className="out-of-stock-overlay">Out of Stock</div>
)}
</div>
<div className="product-info">
<h3 className="product-name">{product.name}</h3>
<div className="rating">
{renderStars(product.rating)}
<span className="rating-text">({product.rating})</span>
</div>
<div className="features">
{product.features.map((feature, index) => (
<span key={index} className="feature-tag">
{feature}
</span>
))}
</div>
<div className="product-footer">
<span className="price">{formatPrice(product.price)}</span>
<button
className="add-to-cart"
disabled={!product.inStock}
>
{product.inStock ? "Add to Cart" : "Unavailable"}
</button>
</div>
</div>
</div>
);
}
Organize components in separate files for better maintainability:
src/
components/
UserProfile/
UserProfile.jsx
UserProfile.css
index.js
ProductCard/
ProductCard.jsx
ProductCard.css
index.js
common/
Button/
Button.jsx
Button.css
index.js
// components/UserProfile/UserProfile.jsx
import React from 'react';
import './UserProfile.css';
function UserProfile() {
return (
<div className="user-profile">
{/* Component JSX */}
</div>
);
}
export default UserProfile;
// components/UserProfile/index.js
export { default } from './UserProfile';
// Usage in other files
import UserProfile from './components/UserProfile';
function App() {
return <UserProfile />;
}
One of React's most powerful features is component composition - building complex UIs by combining simple components.
// Basic Card component
function Card({ children, className = '' }) {
return (
<div className={`card \${className}`}>
{children}
</div>
);
}
// Card Header component
function CardHeader({ children }) {
return (
<div className="card-header">
{children}
</div>
);
}
// Card Body component
function CardBody({ children }) {
return (
<div className="card-body">
{children}
</div>
);
}
// Card Footer component
function CardFooter({ children }) {
return (
<div className="card-footer">
{children}
</div>
);
}
// Composing them together
function UserDashboard() {
return (
<div className="dashboard">
<Card className="user-stats">
<CardHeader>
<h2>User Statistics</h2>
</CardHeader>
<CardBody>
<div className="stats-grid">
<div className="stat">
<span className="stat-number">1,234</span>
<span className="stat-label">Posts</span>
</div>
<div className="stat">
<span className="stat-number">5,678</span>
<span className="stat-label">Followers</span>
</div>
</div>
</CardBody>
</Card>
<Card className="recent-activity">
<CardHeader>
<h2>Recent Activity</h2>
</CardHeader>
<CardBody>
<ActivityList />
</CardBody>
<CardFooter>
<button>View All Activity</button>
</CardFooter>
</Card>
</div>
);
}
// Layout components
function Container({ children, size = 'md' }) {
const sizes = {
sm: 'max-w-4xl',
md: 'max-w-6xl',
lg: 'max-w-7xl',
full: 'max-w-full'
};
return (
<div className={`container mx-auto px-4 \${sizes[size]}`}>
{children}
</div>
);
}
function Row({ children, spacing = 'normal' }) {
const spacingClasses = {
tight: 'space-y-2',
normal: 'space-y-4',
loose: 'space-y-8'
};
return (
<div className={`flex flex-col \${spacingClasses[spacing]}`}>
{children}
</div>
);
}
function Column({ children, width = 'auto' }) {
const widthClasses = {
auto: 'flex-1',
'1/2': 'w-1/2',
'1/3': 'w-1/3',
'2/3': 'w-2/3',
'1/4': 'w-1/4'
};
return (
<div className={`\${widthClasses[width]}`}>
{children}
</div>
);
}
// Using the layout system
function HomePage() {
return (
<Container size="lg">
<Row spacing="loose">
<div className="hero-section">
<h1>Welcome to Our App</h1>
</div>
<div className="flex space-x-8">
<Column width="2/3">
<MainContent />
</Column>
<Column width="1/3">
<Sidebar />
</Column>
</div>
<div className="footer-section">
<Footer />
</div>
</Row>
</Container>
);
}
function LoadingSpinner() {
return (
<div className="loading-spinner">
<div className="spinner"></div>
<p>Loading...</p>
</div>
);
}
function ErrorMessage({ message }) {
return (
<div className="error-message">
<h3>Oops! Something went wrong</h3>
<p>{message}</p>
<button onClick={() => window.location.reload()}>
Try Again
</button>
</div>
);
}
function DataDisplay({ data, loading, error }) {
if (loading) return <LoadingSpinner />;
if (error) return <ErrorMessage message={error} />;
if (!data) return <div>No data available</div>;
return (
<div className="data-display">
{/* Render your data */}
{data.map(item => (
<div key={item.id}>{item.name}</div>
))}
</div>
);
}
function TodoItem({ todo, onToggle, onDelete }) {
return (
<div className={`todo-item \${todo.completed ? 'completed' : ''}`}>
<input
type="checkbox"
checked={todo.completed}
onChange={() => onToggle(todo.id)}
/>
<span className="todo-text">{todo.text}</span>
<button
onClick={() => onDelete(todo.id)}
className="delete-btn"
>
Delete
</button>
</div>
);
}
function TodoList({ todos, onToggle, onDelete }) {
if (todos.length === 0) {
return (
<div className="empty-state">
<p>No todos yet. Add one above!</p>
</div>
);
}
return (
<div className="todo-list">
{todos.map(todo => (
<TodoItem
key={todo.id}
todo={todo}
onToggle={onToggle}
onDelete={onDelete}
/>
))}
</div>
);
}
function FormField({ label, error, children }) {
return (
<div className="form-field">
<label className="form-label">
{label}
{children}
</label>
{error && (
<span className="form-error">{error}</span>
)}
</div>
);
}
function TextInput({ placeholder, value, onChange, ...props }) {
return (
<input
type="text"
className="form-input"
placeholder={placeholder}
value={value}
onChange={(e) => onChange(e.target.value)}
{...props}
/>
);
}
function ContactForm() {
return (
<form className="contact-form">
<FormField label="Name">
<TextInput
placeholder="Enter your name"
value=""
onChange={() => {}}
/>
</FormField>
<FormField label="Email">
<TextInput
placeholder="Enter your email"
value=""
onChange={() => {}}
/>
</FormField>
<button type="submit" className="submit-btn">
Send Message
</button>
</form>
);
}
While you might see class components in older codebases, functional components are now the standard:
import React, { Component } from 'react';
class Welcome extends Component {
render() {
return <h1>Hello, {this.props.name}!</h1>;
}
}
function Welcome({ name }) {
return <h1>Hello, {name}!</h1>;
}
Why functional components are preferred:
❌ Bad - Component doing too much:
function UserDashboard() {
return (
<div>
{/* User profile section */}
<div className="user-profile">
<img src="avatar.jpg" />
<h2>John Doe</h2>
<p>Software Engineer</p>
</div>
{/* Stats section */}
<div className="stats">
<div>Posts: 123</div>
<div>Followers: 456</div>
<div>Following: 789</div>
</div>
{/* Recent posts section */}
<div className="recent-posts">
<h3>Recent Posts</h3>
<div>Post 1...</div>
<div>Post 2...</div>
<div>Post 3...</div>
</div>
{/* Notifications section */}
<div className="notifications">
<h3>Notifications</h3>
<div>Notification 1...</div>
<div>Notification 2...</div>
</div>
</div>
);
}
✅ Good - Split into focused components:
function UserProfile({ user }) {
return (
<div className="user-profile">
<img src={user.avatar} alt={user.name} />
<h2>{user.name}</h2>
<p>{user.role}</p>
</div>
);
}
function UserStats({ stats }) {
return (
<div className="stats">
<div>Posts: {stats.posts}</div>
<div>Followers: {stats.followers}</div>
<div>Following: {stats.following}</div>
</div>
);
}
function RecentPosts({ posts }) {
return (
<div className="recent-posts">
<h3>Recent Posts</h3>
{posts.map(post => (
<div key={post.id}>{post.title}</div>
))}
</div>
);
}
function UserDashboard() {
return (
<div>
<UserProfile user={user} />
<UserStats stats={stats} />
<RecentPosts posts={posts} />
<Notifications notifications={notifications} />
</div>
);
}
❌ Bad:
function Card() {} // Too generic
function UC() {} // Abbreviated
function Component1() {} // Non-descriptive
✅ Good:
function ProductCard() {}
function UserComment() {}
function ShoppingCartItem() {}
❌ Bad - Repeated logic:
function UserCard({ user }) {
const formatDate = (date) => {
return new Date(date).toLocaleDateString();
};
return <div>Joined: {formatDate(user.joinDate)}</div>;
}
function PostItem({ post }) {
const formatDate = (date) => {
return new Date(date).toLocaleDateString();
};
return <div>Posted: {formatDate(post.createdAt)}</div>;
}
✅ Good - Shared utility:
// utils/dateUtils.js
export const formatDate = (date) => {
return new Date(date).toLocaleDateString();
};
// components/UserCard.jsx
import { formatDate } from '../utils/dateUtils';
function UserCard({ user }) {
return <div>Joined: {formatDate(user.joinDate)}</div>;
}
❌ Bad - Complex conditional rendering:
function UserDisplay({ user, showProfile, showStats, showPosts }) {
return (
<div>
{showProfile && (
<div>
<img src={user.avatar} />
<h2>{user.name}</h2>
</div>
)}
{showStats && (
<div>
<div>Posts: {user.postsCount}</div>
<div>Followers: {user.followersCount}</div>
</div>
)}
{showPosts && (
<div>
{user.posts.map(post => (
<div key={post.id}>{post.title}</div>
))}
</div>
)}
</div>
);
}
✅ Good - Composition:
function UserDisplay({ user, children }) {
return (
<div className="user-display">
{children}
</div>
);
}
// Usage
<UserDisplay user={user}>
<UserProfile user={user} />
<UserStats user={user} />
<UserPosts posts={user.posts} />
</UserDisplay>
❌ Wrong:
function MyComponent() {
<div>Hello World</div>; // Missing return
}
✅ Correct:
function MyComponent() {
return <div>Hello World</div>;
}
❌ Wrong:
function myComponent() { // lowercase
return <div>Hello</div>;
}
✅ Correct:
function MyComponent() { // PascalCase
return <div>Hello</div>;
}
❌ Wrong:
function ItemList({ items }) {
return (
<ul>
{items.map(item => (
<li>{item.name}</li> // Missing key
))}
</ul>
);
}
✅ Correct:
function ItemList({ items }) {
return (
<ul>
{items.map(item => (
<li key={item.id}>{item.name}</li>
))}
</ul>
);
}
Create a BusinessCard component that displays:
Build a WeatherWidget component showing:
Create a ProductGallery component with:
Build components for:
Comment - individual commentCommentList - list of commentsCommentForm - add new commentCommentThread - nested commentsFunctional components are the foundation of modern React development:
✅ Components are reusable pieces of UI
✅ Use PascalCase naming convention
✅ Keep components small and focused
✅ Leverage composition for complex UIs
✅ Extract reusable logic into utilities
✅ Always return JSX from your components
In the next lesson, we'll learn about Props and PropTypes - how to pass data between components and make them truly reusable. You'll discover:
Components become truly powerful when they can receive and work with dynamic data - that's where props come in!