Module 4.2: Component Design Patterns

🎯 Learning Objectives
After this lesson, you will master:
- Component Organization: Understand strategies for organizing code folders (Category-based vs. Feature-based).
- Composition Pattern: Techniques using the
childrenprop to create flexible, customizable components instead of rigid inheritance. - CLS Optimization: How to handle images and layout to prevent Cumulative Layout Shift, improving both UX and SEO.
1. Component Organization Patterns
There are many ways to organize folders in a React project. Below are 3 of the most common patterns, ranging from pragmatic to structured.
1.1. Category-based (Organizing by Function)

This is a pattern you often see in projects using modern UI libraries (like Shadcn/UI, Mantine). Components are grouped by their role within the entire application.
Folder structure:
src/
components/
ui/ # Basic, atomic components (Button, Input, Card)
layouts/ # Common layout frames (Header, Sidebar, Footer)
common/ # More complex shared components (LoadingSpinner, ErrorState)
icons/ # SVG iconsPros: Easy to find global/shared components. Cons: Can become hard to manage when complex business logic is intertwined with the UI.
1.2. Feature-based / Colocation (Organizing by Feature)

“Keep things that change together, close together.” Components that solely serve a specific feature (or a particular page) should reside directly within that feature’s folder, not in a general components folder.
Folder structure:
src/
features/ # Major domains
auth/
components/ # Specific to Auth (LoginForm, RegisterForm)
hooks/
types/
product/
components/ # Product forms, product lists, etc.
pages/ # Page structure (Next.js / Vite)
dashboard/
components/ # Widgets exclusive to the dashboard
index.tsxPros: Highly scalable; deleting a feature often means just deleting a single folder. Cons: Requires team discipline to avoid mixing Common and Feature components.
1.3. Atomic Design (A Design Mindset)
Regardless of your chosen folder structure, Atomic Design remains a foundational mindset to “decompose” your UI.

This mindset divides the UI into 5 levels:
- Atoms: Smallest units (Button, Input, Label). -> Corresponds to the
ui/folder. - Molecules: Groups of Atoms (SearchBox = Input + Button). -> Corresponds to
common/orfeatures/folders. - Organisms: Larger groups (Header, ProductList). -> Corresponds to
layouts/orfeatures/folders. - Templates/Pages: Complete pages.
Atomic Design Mindset Example:
// Atom: Button.tsx
interface ButtonProps extends React.PropsWithChildren {}
export default function Button({ children }: ButtonProps) {
return <button className="bg-blue-500 text-white px-4 py-2 rounded">{children}</button>;
}
// Molecule: SearchBox.tsx (Combines Atoms)
export function SearchBox() {
return (
<div className="flex gap-2">
<input className="border p-2" placeholder="Search..." />
<Button>Tìm kiếm</Button>
</div>
);
}2. Composition Pattern
React prioritizes Composition over Inheritance. The core technique involves using the special children prop.
2.1. Flexible Containers with children
Instead of passing content via a string prop (content="..."), use children to pass arbitrary JSX.
// ❌ Limited: Only accepts text
function Box({ content }: { content: string }) {
return <div className="box">{content}</div>;
}
// ✅ Flexible: Accepts anything (Text, HTML, other Components)
interface BoxProps {
children: React.ReactNode;
}
function Box({ children }: BoxProps) {
return <div className="border p-4 rounded bg-white shadow">{children}</div>;
}
// Usage
<Box>
<h2 className="text-xl">Title</h2>
<Button>Action</Button>
</Box>;2.2. Slots Pattern (Multiple Content Areas)
If a component needs multiple distinct content areas (e.g., a Layout with Header, Sidebar, Content), pass components directly as props.
interface AppLayoutProps {
header: React.ReactNode;
sidebar: React.ReactNode;
content: React.ReactNode;
}
function AppLayout({ header, sidebar, content }: AppLayoutProps) {
return (
<div className="min-h-screen">
<div className="top-bar">{header}</div>
<div className="flex">
<div className="w-64">{sidebar}</div>
<div className="flex-1">{content}</div>
</div>
</div>
);
}
// Usage
<AppLayout header={<Header />} sidebar={<Nav />} content={<Dashboard />} />;3. CLS (Cumulative Layout Shift) & Image Handling
CLS is a metric measuring UI stability. A common issue arises when slow-loading images push down content below them, creating a frustrating user experience.

3.1. The Cause
The browser doesn’t know the image dimensions before it finishes loading. It renders with height = 0, then once loaded, renders with height = X px, causing a layout shift.
3.2. The Solution: Set Fixed Dimensions or Aspect Ratio
Method 1: Set specific width/height
// Always set width and height attributes
<img src="avatar.jpg" width="100" height="100" alt="User Avatar" className="rounded-full" />Method 2: Use Aspect Ratio (Tailwind)
If the wrapper has a responsive width, use aspect-ratio to reserve vertical space based on its ratio.
<div className="aspect-video bg-gray-200 w-full overflow-hidden rounded-lg">
<img src="banner.jpg" alt="Banner" className="w-full h-full object-cover" loading="lazy" />
</div>4. Hands-On (Labs)
Lab 1: Universal Modal System
Build an incredibly flexible (reusable) Modal component that works for any situation:
- Variants: Success (Green), Error (Red), Info (Blue).
- Slots: Header, Body (children), Footer.
- Supports closing when clicking outside (overlay click) or pressing Esc (
keydownevent).

💡 Hints:
- Utilize the Composition pattern: The
Modalcomponent acceptschildrento render custom body content.- Props:
isOpen(boolean),onClose(function),title(string),variant(enum).- Use
useEffectto listen for thekeydownevent (ESC key) and callonClose.- Layout: Use Tailwind’s
fixed inset-0for a full-screen overlay.
Lab 2: Pinterest Masonry Layout (Component Organization)
Organize the code for a Pinterest-style “Image Gallery” page.
- Clearly separate components:
PhotoCard(Atom),MasonryGrid(Organism). - Handle images to prevent CLS (using
aspect-videoor setting height).

💡 Hints:
- Apply a Feature-based folder structure: Create a
features/galleryfolder.- Inside
PhotoCard, use animgtag withobject-coverandw-fullclasses.- Use CSS columns (
columns-3) or a Masonry library for a staggered grid layout.
5. Summary
- Component Organization: Choose Category-based (UI/Layouts) for small/medium projects, and Feature-based (Features/Pages) for larger ones. Apply the Atomic mindset to decompose your UI.
- Composition: The key to flexibility in React. Use
childrento pass nested content. - CLS: Always “reserve space” for images using
width/heightoraspect-ratioto prevent layout shifts, making your app look professional and feel fluid.