React 7 ⚛️ Creating Functional Components
A component is a JavaScript function that returns JSX. That is the entire definition. Where earlier React code used class components with render() methods and lifecycle hooks, modern React uses functions that accept props and return elements. The shift to functions was not cosmetic: it removed the this binding problem, eliminated the ceremony of class syntax, enabled hooks for state and side effects, and aligned React with the functional programming style that dominates modern JavaScript.
Functional components are the default and the recommendation. The React documentation presents them first, the ecosystem is built around them, and class components are described as legacy. Writing components as functions means composing them as functions: small, focused, testable, and easy to reason about. A component that does one thing and returns the JSX for that thing is easier to read, test, and reuse than a class that mixes rendering with state management and lifecycle logic.
This chapter covers how to define functional components, the props parameter and destructuring, returning JSX, component naming rules, composition and children, default props, prop types with TypeScript, and the patterns that distinguish a well-designed component from one that is difficult to maintain.
Key point: A functional component is a JavaScript function that returns JSX and accepts a single props argument. Component names must start with a capital letter so JSX distinguishes them from HTML tags. Modern React uses function components exclusively; class components are legacy.
Why functional components exist
The class ceremony problem. Class components require a class declaration, a constructor, super(props), this.state, this.setState, and this binding in event handlers. This is a substantial amount of code that has nothing to do with the component’s actual purpose. Functional components remove all of it: a function that returns JSX, and optionally a hook for state.
The this problem. In class components, this is a source of constant confusion. Event handlers lose this unless bound in the constructor or written as arrow functions. Passing methods to child components requires care. Functional components have no this; closures capture the values they need.
The logic reuse problem. Class components reuse logic through inheritance or higher-order components, both of which create awkward nesting. Hooks, which are only available in functional components, allow logic to be extracted into reusable functions (useFetch, useLocalStorage, useDebounce) that can be shared without wrapping components.
The testing problem. A functional component is a pure function of its props. Given the same props, it returns the same JSX. This makes testing straightforward: render the component with known props and assert on the output. Class components carry state and lifecycle, which complicates testing.
The composition problem. Functional components compose naturally. A component that renders another component is just a function calling a function (through JSX). There is no inheritance hierarchy, no super, no extends. Composition is the primary mechanism for sharing behavior, and functional components make it simple.
a. Defining a functional component
A functional component is a function that returns JSX. The function name must begin with a capital letter.
function Welcome() {
return <h1>Hello, world!</h1>;
}
The component is used like an HTML tag in JSX, but with a capital first letter:
function App() {
return (
<div>
<Welcome />
<Welcome />
</div>
);
}
The capital letter is not stylistic. JSX transforms <Welcome /> into React.createElement(Welcome) and <div> into React.createElement('div'). Lowercase names become strings (HTML tags); capitalized names become references to functions or classes. A component defined as welcome would be treated as an unknown HTML tag, not a component.
Arrow function syntax is also valid and is common for components defined inline or assigned to variables:
const Welcome = () => <h1>Hello, world!</h1>;
const Greeting = ({ name }) => {
return <h1>Hello, {name}!</h1>;
};
Both forms are equivalent. The function declaration form hoists, which matters in some file organization patterns. The arrow function form is concise for simple components.
b. The props parameter
A component receives a single argument, conventionally named props. It is an object containing the attributes passed to the component in JSX.
function Welcome(props) {
return <h1>Hello, {props.name}!</h1>;
}
// Usage
<Welcome name="Alice" />
Destructuring in the parameter list is the common style because it makes the component’s expected props explicit and reduces repetition:
function Welcome({ name, greeting = "Hello" }) {
return <h1>{greeting}, {name}!</h1>;
}
Default values are written in the destructuring pattern. This is the modern replacement for defaultProps, which was deprecated for function components.
Destructuring in the parameter has a subtle trade-off: it hides the full props object, which can be useful for debugging or for passing remaining props through with the rest operator. When both are needed, destructure in the body:
function Button(props) {
const { children, variant = "primary", ...rest } = props;
return (
<button className={`btn btn-${variant}`} {...rest}>
{children}
</button>
);
}
c. Returning JSX
A component returns a single element. If multiple elements are needed at the top level, wrap them in a parent element or a fragment.
// Single element
function Header() {
return <h1>Title</h1>;
}
// Multiple elements wrapped in a div
function Header() {
return (
<div>
<h1>Title</h1>
<p>Subtitle</p>
</div>
);
}
// Multiple elements with a fragment
function Header() {
return (
<>
<h1>Title</h1>
<p>Subtitle</p>
</>
);
}
Returning null is valid and renders nothing. This is useful for conditional rendering where the component decides not to render at all.
function Notification({ message }) {
if (!message) return null;
return <div className="notification">{message}</div>;
}
The return statement and the JSX it returns must be on the same line, or the JSX must be wrapped in parentheses. This is a JavaScript rule about automatic semicolon insertion, not a JSX rule.
// Works
return <h1>Title</h1>;
// Works
return (
<h1>Title</h1>
);
// Does not work: returns undefined
return
<h1>Title</h1>;
d. Component naming and file organization
Component names use PascalCase: UserProfile, ShoppingCart, LoginForm. This distinguishes them from regular functions (camelCase) and from HTML tags (lowercase).
File organization follows conventions that vary by project. A common pattern is one component per file, with the file named after the component:
components/
Button.jsx
UserProfile.jsx
ShoppingCart.jsx
Co-locating related files — the component, its styles, its tests — in a folder is also common:
components/
Button/
Button.jsx
Button.css
Button.test.jsx
The .jsx extension is optional in most build setups but makes it explicit that the file contains JSX. Some projects use .tsx for TypeScript with JSX.
e. Composition and children
Components compose by rendering other components. The children prop is the special prop that contains the JSX between a component’s opening and closing tags.
function Card({ children }) {
return <div className="card">{children}</div>;
}
// Usage
<Card>
<h2>Title</h2>
<p>Body text</p>
</Card>
The children prop is how components wrap arbitrary content. A layout component, a modal, a tooltip, or a button that accepts an icon can all use children to accept whatever the caller provides.
Multiple slots are handled with named props:
function Layout({ header, sidebar, children }) {
return (
<div className="layout">
<header>{header}</header>
<aside>{sidebar}</aside>
<main>{children}</main>
</div>
);
}
// Usage
<Layout header={<h1>Title</h1>} sidebar={<Nav />}>
<Content />
</Layout>
Props that accept elements are a form of composition that is more flexible than children because it allows multiple insertion points.
f. Default props and TypeScript prop types
Before hooks, defaultProps was the mechanism for default values:
function Welcome({ name }) {
return <h1>Hello, {name}!</h1>;
}
Welcome.defaultProps = { name: "Guest" };
This still works but is deprecated for function components. The modern approach is default values in destructuring:
function Welcome({ name = "Guest" }) {
return <h1>Hello, {name}!</h1>;
}
In TypeScript, prop types are defined with an interface or type alias:
interface WelcomeProps {
name: string;
greeting?: string;
onDismiss?: () => void;
}
function Welcome({ name, greeting = "Hello", onDismiss }: WelcomeProps) {
return (
<div>
<h1>{greeting}, {name}!</h1>
{onDismiss && <button onClick={onDismiss}>Dismiss</button>}
</div>
);
}
The ? marks optional props. TypeScript checks that callers provide required props and that the types match. For components that accept children, the type is React.ReactNode:
interface CardProps {
title: string;
children: React.ReactNode;
}
function Card({ title, children }: CardProps) {
return (
<div className="card">
<h2>{title}</h2>
<div>{children}</div>
</div>
);
}
g. Patterns and anti-patterns
Keep components small. A component that renders more than a screenful of JSX is hard to read and test. Extract sections into their own components.
Lift state up. When two components need to share state, move the state to their closest common ancestor and pass it down as props. Functional components make this pattern natural.
Prefer composition over prop drilling. When a deeply nested component needs a value from a distant ancestor, passing it through every intermediate component (prop drilling) is tedious. Context or component composition solves this.
Avoid defining components inside components. A component defined inside another component is recreated on every render, which causes the inner component to remount and lose state. Define components at the top level of a file.
// Wrong: Child is recreated on every render of Parent
function Parent() {
function Child() {
return <p>Child</p>;
}
return <Child />;
}
// Correct: Child is defined once
function Child() {
return <p>Child</p>;
}
function Parent() {
return <Child />;
}
Return early for guard conditions. Instead of nesting conditionals in JSX, return early when a condition makes the component render nothing or a fallback.
function UserProfile({ user }) {
if (!user) return <p>Loading...</p>;
if (user.error) return <p>Error loading user</p>;
return <div>{user.name}</div>;
}
Complete Example Session
// ============================================
// PART 1: MINIMAL FUNCTIONAL COMPONENT
// ============================================
function Welcome() {
return <h1>Hello, world!</h1>;
}
// ============================================
// PART 2: COMPONENT WITH PROPS
// ============================================
function Greeting(props) {
return <h1>Hello, {props.name}!</h1>;
}
// Usage: <Greeting name="Alice" />
// ============================================
// PART 3: DESTRUCTURED PROPS WITH DEFAULTS
// ============================================
function Greeting({ name, greeting = "Hello" }) {
return <h1>{greeting}, {name}!</h1>;
}
// ============================================
// PART 4: COMPONENT RETURNING MULTIPLE ELEMENTS
// ============================================
function UserCard({ name, email }) {
return (
<>
<h2>{name}</h2>
<p>{email}</p>
</>
);
}
// ============================================
// PART 5: COMPONENT WITH CHILDREN
// ============================================
function Card({ title, children }) {
return (
<div className="card">
<h3>{title}</h3>
<div className="card-body">{children}</div>
</div>
);
}
// Usage:
// <Card title="Profile"><p>Content</p></Card>
// ============================================
// PART 6: COMPONENT WITH EVENT HANDLER PROP
// ============================================
function Button({ label, onClick, variant = "primary" }) {
return (
<button className={`btn btn-${variant}`} onClick={onClick}>
{label}
</button>
);
}
// Usage: <Button label="Save" onClick={handleSave} />
// ============================================
// PART 7: COMPOSING COMPONENTS
// ============================================
function Avatar({ src, alt }) {
return <img className="avatar" src={src} alt={alt} />;
}
function UserInfo({ user }) {
return (
<div className="user-info">
<Avatar src={user.avatarUrl} alt={user.name} />
<span>{user.name}</span>
</div>
);
}
// ============================================
// PART 8: EARLY RETURN FOR GUARD CONDITIONS
// ============================================
function UserProfile({ user }) {
if (!user) return <p>Loading...</p>;
if (user.error) return <p>Error: {user.error}</p>;
return <div>{user.name}</div>;
}
// ============================================
// PART 9: TYPESCRIPT PROPS
// ============================================
interface ProductCardProps {
name: string;
price: number;
onAddToCart: () => void;
imageUrl?: string;
}
function ProductCard({ name, price, onAddToCart, imageUrl }: ProductCardProps) {
return (
<div className="product-card">
{imageUrl && <img src={imageUrl} alt={name} />}
<h3>{name}</h3>
<p>${price.toFixed(2)}</p>
<button onClick={onAddToCart}>Add to cart</button>
</div>
);
}
// ============================================
// PART 10: PASSING REMAINING PROPS
// ============================================
function Input({ label, error, ...rest }) {
return (
<label>
{label}
<input {...rest} className={error ? "input error" : "input"} />
{error && <span className="error-message">{error}</span>}
</label>
);
}
// Usage:
// <Input label="Email" type="email" placeholder="you@example.com" />
These ten parts cover the range of functional component patterns: minimal components, props, destructuring with defaults, fragments, children, event handlers, composition, early returns, TypeScript props, and passing remaining props through. Each pattern is idiomatic modern React.
Quick Reference
Component Definition Forms
| Form | Example |
|---|---|
| Function declaration | function App() { return <div />; } |
| Arrow function | const App = () => <div />; |
| Arrow with body | const App = () => { return <div />; }; |
Props Patterns
| Pattern | Syntax |
|---|---|
| Access props object | function C(props) { props.name } |
| Destructure | function C({ name }) { name } |
| Default value | function C({ name = "Guest" }) {} |
| Rest props | function C({ a, ...rest }) {} |
| Children | function C({ children }) {} |
| Named slots | function C({ header, children }) {} |
Return Rules
| Rule | Detail |
|---|---|
| Single root | One element, or fragment, or null |
| Return null | Renders nothing |
| Same line | return <div /> or wrap in parentheses |
| Guard clauses | Early return for loading/error states |
Naming and Organization
| Convention | Example |
|---|---|
| Component name | PascalCase: UserProfile |
| File name | Same as component: UserProfile.jsx |
| One per file | Common but not mandatory |
| Co-located files | Button/Button.jsx, Button/Button.css |
Best Practices
✅ Do This:
function Welcome({ name = "Guest" }) { // Destructure with default
return <h1>Hello, {name}!</h1>;
}
function Card({ title, children }) { // children for composition
return <div><h3>{title}</h3>{children}</div>;
}
function Profile({ user }) {
if (!user) return <p>Loading...</p>; // Early return
return <div>{user.name}</div>;
}
❌ Don’t Do This:
function welcome() { return <h1>Hi</h1>; } // ❌ Lowercase name
function Parent() {
function Child() { return <p>Hi</p>; } // ❌ Nested component
return <Child />;
}
function Item(props) { return <p>{props.item.name}</p>; } // ❌ No destructuring
function Box() {
return
<div>Content</div>; // ❌ Return on separate line
}
Common Pitfalls
| Pitfall | Why It Happens | Fix |
|---|---|---|
| Component renders as HTML tag | Lowercase name | Use PascalCase |
| Return undefined | JSX on line after return | Put on same line or wrap in parentheses |
| Children not rendering | children not included in JSX | Add {children} to the output |
| Component remounts on each render | Defined inside another component | Define at top level |
| Props undefined | Not passed or misspelled | Check the JSX usage |
| Default not applied | Using defaultProps (deprecated) | Use destructuring default |
Real-World Examples
1. Button Component
function Button({ children, onClick, disabled = false }) {
return (
<button onClick={onClick} disabled={disabled}>
{children}
</button>
);
}
2. Layout with Named Slots
function Layout({ header, footer, children }) {
return (
<div>
<header>{header}</header>
<main>{children}</main>
<footer>{footer}</footer>
</div>
);
}
3. List Rendering Component
function UserList({ users }) {
return (
<ul>
{users.map(u => <li key={u.id}>{u.name}</li>)}
</ul>
);
}
4. Conditional Rendering Component
function Alert({ type = "info", message }) {
if (!message) return null;
return <div className={`alert alert-${type}`}>{message}</div>;
}
5. Form Input with Label and Error
function Field({ label, error, ...inputProps }) {
return (
<label>
{label}
<input {...inputProps} />
{error && <span className="error">{error}</span>}
</label>
);
}
6. Card with Composition
function Card({ children, className = "" }) {
return <div className={`card ${className}`}>{children}</div>;
}
7. Avatar with Fallback
function Avatar({ src, alt, size = 40 }) {
return src
? <img src={src} alt={alt} width={size} height={size} />
: <div className="avatar-placeholder" style={{ width: size, height: size }} />;
}
8. Wrapper with Loading State
function AsyncContent({ loading, error, children }) {
if (loading) return <Spinner />;
if (error) return <ErrorMessage message={error} />;
return children;
}
9. TypeScript Generic List
interface ListProps<T> {
items: T[];
renderItem: (item: T) => React.ReactNode;
}
function List<T>({ items, renderItem }: ListProps<T>) {
return <ul>{items.map(renderItem)}</ul>;
}
10. Compound Component
function Tabs({ children }) {
return <div className="tabs">{children}</div>;
}
Tabs.Tab = function Tab({ label, active }) {
return <div className={active ? "tab active" : "tab"}>{label}</div>;
};
Visual
Functional Component Anatomy
┌──────────────────────────────────────────────────────────────┐
│ ANATOMY OF A FUNCTIONAL COMPONENT │
│ │
│ function ProductCard ({ name, price, onAdd }) { │
│ │ │ │ │ │
│ │ │ │ └── Body │
│ │ │ └── Props (destructured) │
│ │ └── PascalCase name │
│ └── Function keyword │
│ │
│ return ( │
│ <div className="card"> │
│ <h3>{name}</h3> │
│ <p>${price}</p> │
│ <button onClick={onAdd}>Add</button> │
│ </div> │
│ ); │
│ } │
│ │
│ The function receives props and returns JSX. │
└──────────────────────────────────────────────────────────────┘
Composition via children
┌──────────────────────────────────────────────────────────────┐
│ children PROP FOR COMPOSITION │
│ │
│ Definition: │
│ function Card({ title, children }) { │
│ return ( │
│ <div className="card"> │
│ <h3>{title}</h3> │
│ <div>{children}</div> │
│ </div> │
│ ); │
│ } │
│ │
│ Usage: │
│ <Card title="Profile"> │
│ <Avatar src={user.avatar} /> │
│ <p>{user.name}</p> │
│ </Card> │
│ │
│ Result: │
│ ┌────────────────────────────────────────────────────────┐ │
│ │ Profile │ │
│ │ ┌──────────────────────────────────────────────────┐ │ │
│ │ │ [Avatar] user.name │ │ │
│ │ └──────────────────────────────────────────────────┘ │ │
│ └────────────────────────────────────────────────────────┘ │
│ │
│ The Card does not know what it contains; the caller decides │
│ the content. This is composition. │
└──────────────────────────────────────────────────────────────┘
Nested Component Anti-Pattern
┌──────────────────────────────────────────────────────────────┐
│ WHY NOT DEFINE COMPONENTS INSIDE COMPONENTS │
│ │
│ WRONG: │
│ function Parent() { │
│ function Child() { return <p>Hi</p>; } │
│ return <Child />; │
│ } │
│ │
│ On every Parent render: │
│ └── A new Child function is created │
│ └── React sees a different component type │
│ └── Unmounts the old Child, mounts a new one │
│ └── State in Child is lost │
│ │
│ CORRECT: │
│ function Child() { return <p>Hi</p>; } │
│ function Parent() { return <Child />; } │
│ │
│ Child is defined once, outside Parent. │
│ React sees the same component type on every render. │
└──────────────────────────────────────────────────────────────┘
Return Statement Rules
┌──────────────────────────────────────────────────────────────┐
│ RETURNING JSX │
│ │
│ VALID: │
│ return <h1>Title</h1>; │
│ └── Same line, works │
│ │
│ VALID: │
│ return ( │
│ <h1>Title</h1> │
│ ); │
│ └── Wrapped in parentheses, works │
│ │
│ INVALID: │
│ return │
│ <h1>Title</h1>; │
│ └── Semicolon inserted after return, returns undefined │
│ │
│ INVALID: │
│ return <h1>Title</h1> <p>Sub</p>; │
│ └── Two root elements, must wrap in fragment or parent │
└──────────────────────────────────────────────────────────────┘
Summary
| Item | Value |
|---|---|
| Definition | A JavaScript function that returns JSX |
| Naming | PascalCase required for JSX to treat it as a component |
| Props | Single object argument; destructure in the parameter |
| Defaults | Destructuring default values (not defaultProps) |
| Root element | Single element, fragment, or null |
| children | Special prop for content between tags |
| Named slots | Props that accept elements |
| Return rules | Same line or wrapped in parentheses |
| Guard conditions | Early return for loading/error states |
| TypeScript | Interface for props; React.ReactNode for children |
| Anti-pattern | Defining components inside components |
Key takeaways:
- A functional component is a function that returns JSX. No class, no
this, norendermethod. The function receives props and returns elements. - Component names must be capitalized. JSX treats lowercase names as HTML tags and capitalized names as component references.
- Props are a single object. Destructuring in the parameter is the idiomatic style and makes the component’s expected props explicit.
- Default values go in the destructuring.
defaultPropsis deprecated for function components. - Return a single root. Wrap multiple elements in a fragment or parent element. Returning
nullrenders nothing. childrenenables composition. It is the mechanism for wrapping arbitrary content. Named props that accept elements extend this to multiple slots.- Keep components small and focused. Extract sections into their own components. Small components are easier to read, test, and reuse.
- Never define a component inside another component. The inner component is recreated on every render, causing state loss and performance problems.
Remember: Functional components are the modern React programming model. A component is a function that takes props and returns JSX, and everything else — state, effects, context — is layered on top with hooks. The simplicity of the definition is the point: there is no ceremony to obscure what the component does. Props flow in, JSX comes out. Composition, whether through children or element-valued props, is how components combine into applications. The rules are few and consistent: capitalize the name, destructure the props, return a single root, and keep the component focused on one responsibility. Components defined at the top level of a file, composed from smaller components, tested with known props, are the building blocks of a React application.
Stop using slow, ad-bloated tool sites! 🤮
🔎 Search “KandZ Tools” on Google to use many professional utilities for free.
KandZ.me is the ultimate minimalist hub for:
✅ Finance (Mortgage, Interest, Inflation)
✅ Tech (Base64, JSON, Dev Suite, IP)
✅ Health (BMI, BMR, TDEE)
✅ Productivity (Timer, Workspace, QR)
⚡️ Fast & Private
🔒 No data leaves your device
💎 100% Free
🔗 Use it now: https://tools.kandz.me
🔖 Bookmark it—you’ll need it later!