Back to Tutorial
Components and Props
Theory Lesson
25 min

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

  1. Component names must start with a capital letter
  2. Components must return JSX (or null)
  3. 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 comment
  • CommentList - list of comments
  • CommentForm - add new comment
  • CommentThread - 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!