Functional Components
Learn how to create reusable, modular functional components that form the building blocks of React applications.
Learning Objectives
- Understand what React components are and why they matter
- Learn how to create functional components
- Master component composition and reusability
- Understand the difference between functional and class components
- Learn component naming conventions and best practices
- Build your first custom component library
Functional Components
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.
What are React Components?
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>
.
The Component Mindset
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} />
Creating Your First Functional Component
Basic Syntax
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>;
Component Rules
- Component names must start with a capital letter
- Components must return JSX (or null)
- Components should be pure functions when possible
❌ Wrong:
function welcome() { // lowercase name return <h1>Hello</h1>; } function Welcome() { // No return statement }
✅ Correct:
function Welcome() { return <h1>Hello</h1>; }
Building Real Components
Let's build some practical components step by step.
1. Simple Greeting Component
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> ); }
2. User Profile Component
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> ); }
3. Product Card Component
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> ); }
Component Organization
File Structure
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
Component File Template
// 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 />; }
Component Composition
One of React's most powerful features is component composition - building complex UIs by combining simple components.
Building a Card Layout System
// 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> ); }
Building a Layout System
// 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> ); }
Advanced Component Patterns
1. Conditional Rendering Components
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> ); }
2. List Rendering Components
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> ); }
3. Form Components
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> ); }
Functional vs Class Components
While you might see class components in older codebases, functional components are now the standard:
Class Component (Old Way)
import React, { Component } from 'react'; class Welcome extends Component { render() { return <h1>Hello, {this.props.name}!</h1>; } }
Functional Component (Modern Way)
function Welcome({ name }) { return <h1>Hello, {name}!</h1>; }
Why functional components are preferred:
- ✅ Less boilerplate code
- ✅ Easier to read and test
- ✅ Better performance
- ✅ Support for React Hooks
- ✅ More aligned with modern JavaScript
Component Best Practices
1. Keep Components Small and Focused
❌ 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> ); }
2. Use Descriptive Component Names
❌ Bad:
function Card() {} // Too generic function UC() {} // Abbreviated function Component1() {} // Non-descriptive
✅ Good:
function ProductCard() {} function UserComment() {} function ShoppingCartItem() {}
3. Extract Reusable Logic
❌ 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>; }
4. Use Composition Over Complex Conditionals
❌ 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>
Common Mistakes and Solutions
1. Forgetting to Return JSX
❌ Wrong:
function MyComponent() { <div>Hello World</div>; // Missing return }
✅ Correct:
function MyComponent() { return <div>Hello World</div>; }
2. Incorrect Component Naming
❌ Wrong:
function myComponent() { // lowercase return <div>Hello</div>; }
✅ Correct:
function MyComponent() { // PascalCase return <div>Hello</div>; }
3. Not Using Keys for Lists
❌ 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> ); }
Practice Exercises
Exercise 1: Personal Business Card
Create a BusinessCard
component that displays:
- Profile photo
- Name and title
- Contact information
- Social media links
Exercise 2: Weather Widget
Build a WeatherWidget
component showing:
- Current temperature
- Weather condition with icon
- Location
- 5-day forecast
Exercise 3: Product Gallery
Create a ProductGallery
component with:
- Grid of product cards
- Search functionality
- Category filtering
- Price sorting
Exercise 4: Comment System
Build components for:
Comment
- individual commentCommentList
- list of commentsCommentForm
- add new commentCommentThread
- nested comments
Summary
Functional 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
What's Next?
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:
- How to pass data to components
- Different types of props
- Default props and prop validation
- Advanced prop patterns
- Building a component API
Components become truly powerful when they can receive and work with dynamic data - that's where props come in!