LearnContact
Lesson 5423 min read

Custom Components

Learn how to design your own components with a thoughtful prop API, and compose them together to build real features.

Introduction

So far, most of the components in this course have been small, single-purpose examples built to demonstrate one concept at a time. Real applications need something more: your own library of custom components that fit your project's specific needs — a styled Card, an Alert banner, a StatBadge, a UserAvatar — each with an interface you designed on purpose.

This lesson is about the design thinking behind building custom components: deciding what a component should accept as props, what should stay fixed, and how to combine your custom pieces into larger features.

What You Will Learn
  • What it means to design a custom component rather than just building one.
  • How to decide what should be configurable through props versus fixed internally.
  • How to build a custom Alert component with a small, clear prop API.
  • How to compose custom components together to build a feature.
  • How to name and organize custom components in a project.

What Makes a Component "Custom"

Every component you write is technically "custom" in the sense that React did not ship it for you. But the term usually refers to components you design deliberately as reusable building blocks for your project — as opposed to a one-off chunk of JSX you happened to extract from a page.

The difference is intent: a custom component has a thought-out interface (its props), a clear responsibility, and is meant to be used more than once, possibly by other developers on your team who never see its internal implementation.

Clear Responsibility

A well-designed component does one job — displaying an alert, a card, a badge — and does it well.

Intentional Interface

Its props are chosen deliberately, not just whatever happened to be convenient at the time.

Encapsulated Details

Consumers of the component do not need to know how it is implemented internally.

Meant to Be Reused

It is designed with more than one call site in mind, even if it only has one today.

Designing a Prop API

The props a component accepts are its public interface — similar to the parameters of a function. Good prop design means thinking from the caller's perspective: what does someone using this component actually need to control, and what names make that obvious?

Questions to Ask When Designing Props

  • What content needs to change between uses? (a title, a message, an icon)
  • What behavior needs to change? (an onClick handler, a variant, a size)
  • What should have a sensible default so most callers do not have to think about it?
  • Which props are required, and which are optional?
  • Does this prop name clearly describe what it controls, without reading the implementation?

Configurable vs Fixed

Not everything about a component should be a prop. Turning every possible detail into a prop makes a component confusing to use and harder to maintain. The goal is to expose exactly what varies in practice, and keep everything else fixed inside the component.

Good: Focused Props

  • title, message, variant, onClose — each one clearly changes something meaningful.
  • Internal padding, font, and border-radius stay fixed and consistent.
  • A small, memorable API that is easy to document and easy to use correctly.

Risky: Over-Configurable

  • padding, fontSize, borderRadius, shadowColor all exposed as props.
  • Every call site can end up looking slightly different and inconsistent.
  • The component becomes harder to reason about and to keep visually consistent.

Building a Custom Alert Component

Consider a custom Alert component used to show success, warning, or error messages throughout an app. Its prop API should expose exactly what varies between uses: the message, the type of alert, and an optional close handler.

components/Alert.jsx
function Alert({ type = 'info', title, message, onClose }) {
  const colors = {
    info: '#3b82f6',
    success: '#22c55e',
    warning: '#f59e0b',
    danger: '#ef4444',
  };

  return (
    <div style={{ borderLeft: `4px solid ${colors[type]}`, padding: '12px 16px' }}>
      {title && <strong>{title}</strong>}
      <p>{message}</p>
      {onClose && <button onClick={onClose}>Dismiss</button>}
    </div>
  );
}

export default Alert;

Notice the design choices: type defaults to 'info' so most callers do not need to think about it, title and onClose are optional since not every alert needs a heading or a close button, and the internal padding and border style stay fixed so every Alert looks consistent.

Using the Alert Component
<Alert
  type="success"
  title="Saved!"
  message="Your changes have been saved successfully."
  onClose={() => setShowAlert(false)}
/>
Rendered Output
[green left border]
Saved!
Your changes have been saved successfully. [Dismiss]

Composing Custom Components

The real power of custom components shows up when you combine several of them to build a feature. A "Notifications Panel" feature, for instance, might be built entirely from smaller custom components you already designed.

NotificationsPanel.jsx
function NotificationsPanel({ notifications, onDismiss }) {
  return (
    <Card title="Notifications">
      {notifications.map((note) => (
        <Alert
          key={note.id}
          type={note.type}
          message={note.message}
          onClose={() => onDismiss(note.id)}
        />
      ))}
    </Card>
  );
}

Here, NotificationsPanel does not know or care how Card or Alert are implemented internally — it simply composes them using their public prop APIs, exactly the way you would use any built-in HTML element.

Naming and Organizing Components

As a project grows past a handful of components, a consistent file structure and naming scheme keeps things easy to find.

components/ folder

A dedicated top-level folder keeps reusable pieces separate from page-specific code.

PascalCase Names

Component names and their files use PascalCase, like Alert.jsx or NotificationsPanel.jsx.

One Component per File

Each file exports one primary component, making it easy to locate and import.

Group by Feature (Optional)

Larger apps sometimes group components into subfolders by feature area, like notifications/ or forms/.

Common Mistakes

Avoid These Mistakes
  • Exposing every visual detail as a prop, leading to inconsistent usage across the app.
  • Naming props after implementation details (isBlueBox) instead of meaning (variant="info").
  • Copy-pasting a similar block of JSX repeatedly instead of extracting it into a custom component.
  • Giving a component too many responsibilities at once instead of composing smaller ones together.
  • Skipping sensible defaults, forcing every caller to pass the same values repeatedly.

Best Practices

  • Design props from the caller's point of view, not from how the component happens to be built.
  • Give every optional prop a sensible default value.
  • Keep one component responsible for one clear job.
  • Compose small custom components together rather than building giant, monolithic ones.
  • Keep a consistent naming and folder convention as your components/ directory grows.

Frequently Asked Questions

How is a "custom component" different from any other component?

Technically all authored components are custom, but the term usually implies deliberate design: a clear prop interface meant to be reused, rather than an incidental extraction from a page.

How many props is too many?

There is no fixed number, but if you find yourself struggling to remember what each prop does, or callers frequently pass the same combination, it may be time to simplify or split the component.

Should styling be a prop or fixed inside the component?

Prefer a small set of meaningful options (like a variant prop) over exposing raw style values, so every usage stays visually consistent.

Can a custom component use other custom components internally?

Yes, this is normal and encouraged. NotificationsPanel using Card and Alert internally is a good example of composition.

Where should I put shared custom components in a project?

A top-level components/ folder is the most common convention, optionally organized into subfolders by feature for larger projects.

Key Takeaways

  • A custom component is defined by deliberate design, not just by being author-written.
  • Good prop APIs expose what varies meaningfully and hide everything else.
  • Sensible defaults make a component easier and safer to use.
  • Custom components compose together the same way HTML elements do.
  • Consistent naming and folder structure keep growing projects manageable.

Summary

Designing custom components well means thinking like an API designer: choosing clear, minimal props that expose exactly what varies, giving sensible defaults, and keeping everything else consistent and fixed. Composing several well-designed custom components is how real React features get built.

In this lesson, you learned what separates a deliberately designed custom component from an incidental one, built a custom Alert component, composed it into a larger feature, and covered naming and organization conventions. Next, you will dig deeper into what makes a component genuinely reusable across an entire codebase.

Lesson 54 Completed
  • You can design a thoughtful prop API for your own components.
  • You know how to decide what should be configurable versus fixed.
  • You built a custom Alert component and composed it into a feature.
  • You are ready to explore reusable component design in more depth.
Next Lesson →

Reusable Components