| |

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

FormExample
Function declarationfunction App() { return <div />; }
Arrow functionconst App = () => <div />;
Arrow with bodyconst App = () => { return <div />; };

Props Patterns

PatternSyntax
Access props objectfunction C(props) { props.name }
Destructurefunction C({ name }) { name }
Default valuefunction C({ name = "Guest" }) {}
Rest propsfunction C({ a, ...rest }) {}
Childrenfunction C({ children }) {}
Named slotsfunction C({ header, children }) {}

Return Rules

RuleDetail
Single rootOne element, or fragment, or null
Return nullRenders nothing
Same linereturn <div /> or wrap in parentheses
Guard clausesEarly return for loading/error states

Naming and Organization

ConventionExample
Component namePascalCase: UserProfile
File nameSame as component: UserProfile.jsx
One per fileCommon but not mandatory
Co-located filesButton/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

PitfallWhy It HappensFix
Component renders as HTML tagLowercase nameUse PascalCase
Return undefinedJSX on line after returnPut on same line or wrap in parentheses
Children not renderingchildren not included in JSXAdd {children} to the output
Component remounts on each renderDefined inside another componentDefine at top level
Props undefinedNot passed or misspelledCheck the JSX usage
Default not appliedUsing 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

ItemValue
DefinitionA JavaScript function that returns JSX
NamingPascalCase required for JSX to treat it as a component
PropsSingle object argument; destructure in the parameter
DefaultsDestructuring default values (not defaultProps)
Root elementSingle element, fragment, or null
childrenSpecial prop for content between tags
Named slotsProps that accept elements
Return rulesSame line or wrapped in parentheses
Guard conditionsEarly return for loading/error states
TypeScriptInterface for props; React.ReactNode for children
Anti-patternDefining components inside components

Key takeaways:

  • A functional component is a function that returns JSX. No class, no this, no render method. 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. defaultProps is deprecated for function components.
  • Return a single root. Wrap multiple elements in a fragment or parent element. Returning null renders nothing.
  • children enables 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!