What Are Function Components?
Imagine you're building a house. Instead of constructing everything as one massive structure, you create separate rooms that you can arrange and connect. React function components work similarly - they're reusable pieces of your user interface that you can combine in different ways to create complete applications.
Let's start with a simple example to understand what a function component looks like:
// A basic function component
function Welcome() {
return (
<div className="welcome-message">
<h1>Welcome to Our App!</h1>
<p>We're glad you're here.</p>
</div>
);
}
// Using the component
const rootElement = document.getElementById('root');
ReactDOM.createRoot(rootElement).render(<Welcome />);
Notice a few important details about this component:
1. It's just a regular JavaScript function, but it starts with a capital letter (PascalCase naming)
2. It returns a single JSX element (which can contain other elements inside it)
3. We use it like an HTML tag when rendering
The Anatomy of a Function Component
Let's break down the essential parts of a function component by creating something more substantial:
// A more detailed component example
function ProductCard() {
// Components can contain JavaScript logic
const isOnSale = true;
const discount = 20;
// They must return a single JSX element
return (
// This div is our wrapper element
<div className="product-card">
<img
src="/product-image.jpg"
alt="Product"
className="product-image"
/>
<div className="product-info">
<h2>Awesome Product</h2>
<p>$99.99</p>
{/* Conditional rendering example */}
{isOnSale && (
<span className="sale-badge">
{discount}% Off!
</span>
)}
</div>
</div>
);
}
Showing and Hiding Content
Sometimes you don't want to show everything all the time. React provides several ways to conditionally render content, similar to how you might choose which furniture to place in different rooms based on their purpose:
function NotificationPanel({ messages, isLoggedIn }) {
// Method 1: Return null to show nothing
if (!isLoggedIn) {
return null;
}
// Method 2: Using ternary operators
return (
<div className="notifications">
<h2>Notifications</h2>
{/* Show different content based on messages */}
{messages.length > 0 ? (
<ul className="message-list">
{messages.map(msg => (
<li key={msg.id}>{msg.text}</li>
))}
</ul>
) : (
<p>No new notifications</p>
)}
{/* Method 3: Using && for simple show/hide */}
{messages.length > 5 && (
<button className="mark-all-read">
Mark All as Read
</button>
)}
</div>
);
}
Building Complex UIs with Nested Components
Just as a house has rooms within rooms (like a bathroom within a master suite), React components can contain other components. This nesting allows us to build complex interfaces while keeping our code organized and maintainable:
// A small, focused component for a single navigation link
function NavLink({ href, text, isSelected }) {
return (
<li className={isSelected ? 'selected' : ''}>
<a href={href}>{text}</a>
</li>
);
}
// A component that uses NavLink components
function NavLinks() {
return (
<ul className="nav-links">
<NavLink
href="/home"
text="Home"
isSelected={true}
/>
<NavLink
href="/products"
text="Products"
isSelected={false}
/>
<NavLink
href="/about"
text="About"
isSelected={false}
/>
</ul>
);
}
// A main navigation component that uses NavLinks
function Navigation() {
return (
<nav className="main-nav">
<h1>Our Store</h1>
<NavLinks />
</nav>
);
}
Organizing Components Across Files
As your application grows, you'll want to organize your components into separate files, like organizing different blueprints for different parts of a house. Here's how to structure your components:
// NavLink.jsx
function NavLink({ href, text, isSelected }) {
return (
<li className={isSelected ? 'selected' : ''}>
<a href={href}>{text}</a>
</li>
);
}
export default NavLink;
// NavLinks.jsx
import NavLink from './NavLink';
function NavLinks() {
return (
<ul className="nav-links">
<NavLink
href="/home"
text="Home"
isSelected={true}
/>
<NavLink
href="/products"
text="Products"
isSelected={false}
/>
</ul>
);
}
export default NavLinks;
// Navigation.jsx
import NavLinks from './NavLinks';
function Navigation() {
return (
<nav className="main-nav">
<h1>Our Store</h1>
<NavLinks />
</nav>
);
}
export default Navigation;
This organization provides several benefits:
1. Each component is focused and easy to understand
2. Components can be reused across different parts of your application
3. Files are smaller and easier to maintain
4. Multiple developers can work on different components simultaneously
A Complete Example: Building a Product Listing Page
Let's put everything together in a real-world example:
// ProductCard.jsx
function ProductCard({ product }) {
const { name, price, image, inStock } = product;
if (!product) {
return null; // Show nothing if no product data
}
return (
<div className="product-card">
<img src={image} alt={name} />
<div className="product-info">
<h3>{name}</h3>
<p className="price">${price}</p>
{inStock ? (
<button className="buy-button">
Add to Cart
</button>
) : (
<span className="out-of-stock">
Out of Stock
</span>
)}
</div>
</div>
);
}
// ProductGrid.jsx
function ProductGrid({ products }) {
return (
<div className="product-grid">
{products.map(product => (
<ProductCard
key={product.id}
product={product}
/>
))}
</div>
);
}
// ProductPage.jsx
function ProductPage() {
return (
<div className="product-page">
<Navigation />
<main>
<h1>Our Products</h1>
<ProductGrid products={productData} />
</main>
<Footer />
</div>
);
}
Best Practices for Function Components
As you work with function components, keep these guidelines in mind:
// DO: Keep components focused and single-purpose
function UserAvatar({ user }) {
return (
<img
src={user.avatar}
alt={`${user.name}'s avatar`}
className="avatar"
/>
);
}
// DON'T: Create components that do too much
function UserProfile({ user }) {
// This component is doing too many things!
return (
<div>
<UserAvatar user={user} />
<UserInfo user={user} />
<UserPosts user={user} />
<UserFriends user={user} />
<UserSettings user={user} />
</div>
);
}
// DO: Use clear, descriptive names
function NotificationBell({ count }) {
return (
<div className="notification-bell">
🔔 {count}
</div>
);
}
// DON'T: Use vague or abbreviated names
function NB({ n }) { // Avoid this!
return (
<div className="nb">
🔔 {n}
</div>
);
}