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:
| Component | Responsibility |
|---|---|
Header | Branding and top-level navigation |
MainContent | Organizing the main page sections |
CardsSection | Rendering a related group of cards |
BottomNavigation | Navigation 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.
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
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:
| Concept | Role |
|---|---|
| Props | Pass data and callbacks into a component |
| State | Stores information that can change the UI |
| Composition | Builds 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:
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.
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.
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>
);
}
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.
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.
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.
export default function ScrollableCardRow({ items }) {
return (
<div className="card-row">
{items.map((item) => (
<ProductCard key={item.id} product={item} />
))}
</div>
);
}
.card-row {
display: flex;
gap: 16px;
overflow-x: auto;
}
.product-card {
flex: 0 0 280px;
}
Responsibility split:
| Component | Owns |
|---|---|
ScrollableCardRow | Layout, gap and scrolling behavior |
ProductCard | Image, 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.
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.
Grouping Related Content into Sections
A section component gives related content a consistent structure.
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.
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 large | Too fragmented |
|---|---|
| Mixes unrelated responsibilities | Creates unnecessary wrappers |
| Becomes difficult to reuse | Makes the hierarchy harder to follow |
| Makes testing and debugging harder | Splits 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
| Mistake | Better approach |
|---|---|
| Building the whole page in one component | Split the page into responsible sections |
| Repeating the same menu or card markup | Create a reusable component and map data |
| Putting scrolling logic inside every card | Let the parent container own scrolling |
| Mixing API, filtering and display logic in a card | Separate the data container from the presentational card |
| Giving components vague names | Use names that describe purpose |
| Letting one component handle unrelated tasks | Apply the Single Responsibility Principle |
| Hard-coding expanded and collapsed layouts | Drive 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