Skip to main content

Component Design

Component design is the process of breaking a user interface into small, focused, reusable parts that can be combined into larger screens.

Large Page

Smaller Components

Reusable UI System

Scalable Frontend Architecture

One-line idea: Do not design one large page first; design a system of components that work together.

Core Concepts

Think in Components, Not Full Pages

Instead of treating an entire screen as one component, identify sections with separate responsibilities.

App
├── Header
├── MainContent
│ ├── FeaturedSection
│ └── CardsSection
└── BottomNavigation

Each component has a clear job:

ComponentResponsibility
HeaderBranding and top-level navigation
MainContentOrganizing the main page sections
CardsSectionRendering a related group of cards
BottomNavigationNavigation actions at the bottom

This structure makes the UI easier to understand, debug, redesign and extend.

Component Hierarchy

A component hierarchy describes how larger components contain and coordinate smaller components.

App.jsx
export default function App() {
return (
<div className="app">
<Header />

<main>
<FeaturedSection />
<CardsSection />
</main>

<BottomNavigation />
</div>
);
}

The hierarchy helps answer:

  • Which component owns this part of the UI?
  • Which components repeat?
  • Which component controls the layout?
  • Where should state and behavior live?

Single Responsibility Principle

The Single Responsibility Principle (SRP) means a component should have one clear responsibility.

A button component can be responsible for:

  • rendering its content
  • applying button styles
  • handling its click interaction
Button.jsx
export default function Button({ children, onClick }) {
return (
<button className="button" onClick={onClick}>
{children}
</button>
);
}

The same button should not also manage unrelated concerns such as authentication, API requests, routing, notifications and theme changes.

One component + one clear responsibility

Easier reuse, testing, debugging and maintenance

Separation of Concerns

Separation of concerns keeps different types of work in the components that own them.

For example:

Card               -> displays content
CardRow -> controls horizontal scrolling
CardContainer -> handles data and filtering
MenuButton -> triggers menu state changes
Sidebar -> renders the menu state

A component should not take responsibility for behavior that belongs to its parent or container.

Props, State and Composition

These three ideas work together in component design:

ConceptRole
PropsPass data and callbacks into a component
StateStores information that can change the UI
CompositionBuilds larger components from smaller components
Props provide input
State chooses the current UI
Composition assembles the final screen

Readability Matters

A component system should be easy for another developer to understand.

Readable component design uses:

  • small focused components
  • meaningful names
  • predictable structure
  • limited nesting
  • organized layouts

Clear names:

<ProductCard />
<SidebarMenu />
<UserProfile />

Unclear names:

<Data />
<Stuff />
<ContainerThing />

A component name should communicate what the component represents or does.

Practical Component Patterns

Decomposing a Page

Start by identifying visually and behaviorally independent sections.

Suppose a dashboard contains:

Dashboard
├── Header
│ ├── Logo
│ └── HeaderActions
├── Sidebar
│ └── MenuItem × N
└── Content
├── SummarySection
└── ProductRow
└── ProductCard × N

Then create the screen by composing those parts:

Dashboard.jsx
export default function Dashboard() {
return (
<div className="dashboard">
<Header />

<div className="dashboard__body">
<Sidebar />

<main>
<SummarySection />
<ProductRow />
</main>
</div>
</div>
);
}

The page component describes the layout while child components handle their own focused UI.

Composing a Header

The header acts as a container for smaller pieces.

Header.jsx
export default function Header() {
return (
<header className="header">
<Logo />

<div className="header__right">
<SearchButton />
<ProfileButton />
<MenuButton />
</div>
</header>
);
}

This keeps branding, actions and menu behavior separate instead of placing all header logic in one large component.

Rendering Reusable Menu Items

Repeated UI should usually come from reusable components and data rather than manually duplicated markup.

Sidebar.jsx
const menuItems = [
{ id: 1, label: "Home", href: "/" },
{ id: 2, label: "Products", href: "/products" },
{ id: 3, label: "Profile", href: "/profile" },
];

export default function Sidebar() {
return (
<nav className="sidebar">
{menuItems.map((item) => (
<MenuItem key={item.id} item={item} />
))}
</nav>
);
}
MenuItem.jsx
export default function MenuItem({ item }) {
return (
<a className="menu-item" href={item.href}>
{item.label}
</a>
);
}

Why this is better:

  • avoids duplicated markup
  • keeps every item visually consistent
  • makes new items easy to add
  • allows one component update to affect every item

Presentational Card and Data Container

A reusable card should mainly display the data it receives.

ProductCard.jsx
export default function ProductCard({ product }) {
return (
<article className="product-card">
<img src={product.image} alt={product.title} />
<h3>{product.title}</h3>
<p>{product.description}</p>
</article>
);
}

The parent or container handles the list, state, filtering or data-loading responsibility.

ProductList.jsx
export default function ProductList({ products }) {
return (
<section className="product-list">
{products.map((product) => (
<ProductCard key={product.id} product={product} />
))}
</section>
);
}
ProductList owns the collection

ProductCard receives one product

ProductCard renders only that product

This separation makes the card reusable in multiple sections.

Keep Scrolling in the Container

If several cards should scroll horizontally, scrolling belongs to their container—not to each card.

ScrollableCardRow.jsx
export default function ScrollableCardRow({ items }) {
return (
<div className="card-row">
{items.map((item) => (
<ProductCard key={item.id} product={item} />
))}
</div>
);
}
styles.css
.card-row {
display: flex;
gap: 16px;
overflow-x: auto;
}

.product-card {
flex: 0 0 280px;
}

Responsibility split:

ComponentOwns
ScrollableCardRowLayout, gap and scrolling behavior
ProductCardImage, title, description and card styling

The card does not need to know whether it appears in a carousel, grid or dashboard row.

State-Driven Menu Expansion

State-driven UI renders different layouts based on the current state.

DashboardLayout.jsx
import { useState } from "react";

export default function DashboardLayout() {
const [isMenuExpanded, setIsMenuExpanded] = useState(false);

return (
<div className="dashboard-layout">
<Header onMenuClick={() => setIsMenuExpanded((current) => !current)} />

{isMenuExpanded ? <ExpandedMenu /> : <CollapsedMenu />}

<DashboardContent />
</div>
);
}
Menu button clicked

isMenuExpanded changes

React renders ExpandedMenu or CollapsedMenu

This pattern is common in sidebars, dashboards, mobile navigation and responsive layouts.

A section component gives related content a consistent structure.

Section.jsx
export default function Section({ title, children }) {
return (
<section className="section">
<h2>{title}</h2>
<div className="section__content">{children}</div>
</section>
);
}
<Section title="Recommended Products">
<ProductList products={recommendedProducts} />
</Section>

<Section title="Recently Viewed">
<ProductList products={recentProducts} />
</Section>

This pattern improves layout consistency, readability and responsiveness.

Component Composition

Composition builds flexible components by combining smaller parts.

Card.jsx
export default function Card({ children }) {
return <article className="card">{children}</article>;
}
<Card>
<CardHeader title="Premium Plan" />
<CardBody>Includes all advanced features.</CardBody>
<CardFooter>
<Button>Choose plan</Button>
</CardFooter>
</Card>

The parent defines the arrangement while smaller components provide focused pieces of UI.

Benefits of composition:

  • flexible layouts
  • reusable building blocks
  • easier updates
  • cleaner component structure

Benefits, Trade-Offs and Common Mistakes

Benefits

  • Smaller components are easier to understand and debug.
  • Reusable components reduce duplicated code.
  • Focused responsibilities make testing easier.
  • Data-driven rendering keeps repeated UI consistent.
  • Composition helps the system grow without creating giant components.
  • Clear names and predictable hierarchy improve team collaboration.

Design trade-offs

Too largeToo fragmented
Mixes unrelated responsibilitiesCreates unnecessary wrappers
Becomes difficult to reuseMakes the hierarchy harder to follow
Makes testing and debugging harderSplits simple UI without a clear benefit

The goal is not to create the maximum number of components. The goal is to create components with clear boundaries and useful responsibilities.

Common mistakes

MistakeBetter approach
Building the whole page in one componentSplit the page into responsible sections
Repeating the same menu or card markupCreate a reusable component and map data
Putting scrolling logic inside every cardLet the parent container own scrolling
Mixing API, filtering and display logic in a cardSeparate the data container from the presentational card
Giving components vague namesUse names that describe purpose
Letting one component handle unrelated tasksApply the Single Responsibility Principle
Hard-coding expanded and collapsed layoutsDrive the UI from state

Interview Revision

Quick Revision Checklist

  • Break a screen into sections with clear responsibilities.
  • Represent the UI as a predictable component hierarchy.
  • Use props to pass data and callbacks.
  • Use state when a component's UI changes over time.
  • Render repeated items from data using a reusable component.
  • Keep display components focused on presentation.
  • Keep collection, filtering and data logic in a parent or container.
  • Let layout containers own behavior such as scrolling.
  • Use meaningful component names.
  • Apply SRP to avoid components with unrelated responsibilities.
  • Group related content into reusable sections.
  • Use composition to build flexible components from smaller parts.

Frequently Asked Interview Questions

1. What is component design?

Component design is the process of dividing a UI into small components with clear responsibilities and composing them into larger screens. The goal is readable, reusable and scalable frontend code.

2. How do you decide whether to create a component?

Create a component when a part of the UI has a clear responsibility, repeats in multiple places, contains meaningful independent behavior or helps simplify a large parent component.

3. What is the Single Responsibility Principle in React?

It means a component should focus on one responsibility. A card displays card content, a container manages the card collection, and a scrollable row controls scrolling behavior.

4. Why should repeated items be data-driven?

Mapping data to a reusable component avoids duplicated markup, keeps styling consistent and makes items easier to add or update.

5. What is separation of concerns in component design?

It means keeping unrelated responsibilities separate. For example, a card renders content while its parent handles fetching, filtering, layout or scrolling.

6. What is the difference between props and state?

Props are inputs received from a parent. State is information owned by a component that can change and cause the UI to render differently.

7. What is component composition?

Composition builds a larger component by combining smaller components. For example, a card can be composed from CardHeader, CardBody and CardFooter.

8. How would you design an expandable sidebar?

Store whether the menu is expanded in state. Let a menu button update that state, then conditionally render the expanded or collapsed sidebar.

9. Why should a card not control its container's scrolling?

Scrolling is a layout responsibility shared by the group of cards. Keeping it in the parent allows the same card to be reused in a horizontal row, grid or another layout.

Memory Trick

Split -> Focus -> Reuse -> Compose

Split the page into logical sections
Focus each component on one responsibility
Reuse repeated UI through props and data
Compose small components into complete screens

One-Line Summary

Good component design turns large pages into focused, reusable components that form a readable and scalable UI system.

Final Mental Model

Identify the page sections

Give each section one responsibility

Extract repeated UI into reusable components

Pass data through props

Drive changing UI with state

Keep layout behavior in parent containers

Compose the components into the final screen