Component Thinking: How React Changed My Entire Design Approach
Hey there, Coding Chefs! šØāš»
Ever built a website where you had to copy the same header HTML to 15 different pages? Then later, when you needed to add just one link to that header, you had to hunt down and update all 15 files? Yeah, that's the nightmare I'm talking about.
If you're a developer who's been building websites or web apps, you've probably experienced this pain. You start with good intentions building clean, working pages. But as your project grows, you realize you're duplicating the same pieces of code everywhere. Navigation bars, user cards, buttons, forms - the same HTML and CSS scattered across multiple files.
What starts as a quick copy-paste solution slowly becomes a maintenance nightmare. Want to change a button style? Hope you remember all the places you used it. Need to add a new navigation item? Get ready for some serious find-and-replace action.
There's a better way to think about building user interfaces - something called "component thinking." And for me, learning this concept through React didn't just change how I wrote code; it completely rewired how I approach design and development.
The intention of this article is to walk you through this transformation - how I went from copying and pasting code everywhere to building reusable, maintainable component systems. Whether you're using React, Vue, Angular, or even vanilla JavaScript, this mindset shift will change how you build interfaces forever.
Without further ado, let's get cooking,
The Copy-Paste Nightmare
Picture this: You're excited about a new project. You dive straight into coding, building beautiful pages with HTML and CSS. Everything looks great, users are happy, and you feel like a rockstar developer.
Then comes the inevitable request: "Can you update the header design across all pages?"
Suddenly, you realize you've copied the same header code to 15 different files. What should be a 5-minute change becomes a 2-hour hunt-and-replace mission, and you're terrified you'll miss one and break the entire design consistency.
This was my reality when I built "Lauchat" - a chat platform for university students during the holidays. I was so eager to build something real that I jumped straight into implementation without thinking about reusability or maintainability.
Here's what my approach looked like:
<!-- page1.html -->
<div class="header">
<div class="logo">Lauchat</div>
<div class="nav">
<a href="/home">Home</a>
<a href="/messages">Messages</a>
<a href="/profile">Profile</a>
</div>
<div class="user-info">
<span>Welcome, John</span>
<img src="avatar.jpg" alt="User Avatar" />
</div>
</div>
<!-- page2.html -->
<div class="header">
<div class="logo">Lauchat</div>
<div class="nav">
<a href="/home">Home</a>
<a href="/messages">Messages</a>
<a href="/profile">Profile</a>
</div>
<div class="user-info">
<span>Welcome, John</span>
<img src="avatar.jpg" alt="User Avatar" />
</div>
</div>
<!-- ...and 13 more files with the exact same code -->
Every page had the same header, the same navigation, the same user cards, the same everything. When I needed to add a notification icon to the header, I had to update 15 different files. When I wanted to change the styling, I had to hunt down every instance.
The codebase became bloated, heavy, and incredibly slow to maintain. Adding new features felt like walking through a minefield - one wrong move and something else would break.
The React Revelation
Then I discovered React, and everything changed.
At first, I didn't understand the hype. "Why do I need this complex framework when I can just write HTML and CSS?" I thought. But when I learned about components, something clicked in my brain that fundamentally altered how I think about building interfaces.
React introduced me to a simple but powerful concept: reusable, self-contained pieces of UI.
Here's how that same header would look as a React component:
// Header.jsx
function Header({ username, avatar }) {
return (
<div className="header">
<div className="logo">Lauchat</div>
<Navigation />
<UserInfo username={username} avatar={avatar} />
</div>
);
}
// Navigation.jsx
function Navigation() {
return (
<div className="nav">
<a href="/home">Home</a>
<a href="/messages">Messages</a>
<a href="/profile">Profile</a>
</div>
);
}
// UserInfo.jsx
function UserInfo({ username, avatar }) {
return (
<div className="user-info">
<span>Welcome, {username}</span>
<img src={avatar} alt="User Avatar" />
</div>
);
}
// Usage in any page
function HomePage() {
return (
<div>
<Header username="John" avatar="avatar.jpg" />
{/* Page content */}
</div>
);
}
Suddenly, that 15-file update nightmare became a single change in one component file. Need to add a notification icon? Update the Header component once, and it appears everywhere. Want to change the navigation? Modify the Navigation component, and every page updates automatically.
The Mental Shift: From Pages to Components
Learning React didn't just teach me a new syntax - it completely rewired how I think about user interfaces.
Before React (Page Thinking):
I designed entire pages as monolithic blocks
Repeated elements were copy-pasted
Changes required hunting through multiple files
Inconsistencies crept in over time
After React (Component Thinking):
I break designs into small, reusable pieces
Each piece has a single responsibility
Changes happen in one place and propagate everywhere
Consistency is enforced by the architecture
This shift changed everything about my design process. Now, when I look at a Figma design, I don't see pages - I see components.
Looking at a typical landing page, I might identify:
Header component
Hero section component
Feature card component (reused 3 times)
Testimonial component (reused 5 times)
Footer component
Each component becomes a building block that I can use, reuse, and combine to create complex interfaces.
My Component Design Framework
Over time, I developed a simple framework for deciding what should be a component:
1. The Reusability Test
Question: Will this UI pattern appear in more than one place?
If yes, it's probably a component. Even if you're not sure, erring on the side of componentization usually pays off.
2. The Responsibility Test
Question: Does this piece of UI have a single, clear purpose?
A button that submits a form? Component. A card that displays user information? Component. A section that does five different things? Probably needs to be broken down.
3. The Independence Test
Question: Can this piece of UI function on its own with just props?
Good components are like pure functions - given the same inputs, they produce the same output. They don't depend on global state or external side effects to function.
4. The Naming Test
Question: Can I give this a clear, descriptive name?
If you can't name it clearly, it probably doesn't have a clear purpose. "UserProfileCard" is good. "LeftSidebarThingWithStuff" is not.
Real Impact on Development
The transformation wasn't just theoretical - it had immediate, practical benefits:
Development Speed: Instead of building the same UI elements repeatedly, I could focus on building new components and composing existing ones.
Maintenance: Bug fixes and design updates became single-file changes instead of project-wide hunts.
Consistency: When everything uses the same Button component, all buttons behave consistently across the application.
Testing: I could test components in isolation, making it easier to catch and fix issues.
Collaboration: Other developers could use my components without understanding their implementation details.
A Practical Example
Let me show you how this plays out with a common pattern - user cards.
Old Approach (Copy-Paste Hell):
<!-- In messages.html -->
<div class="user-card">
<img src="user1.jpg" alt="User 1" />
<div class="user-info">
<h3>John Doe</h3>
<p class="status">Online</p>
<button class="message-btn">Send Message</button>
</div>
</div>
<!-- In contacts.html -->
<div class="user-card">
<img src="user2.jpg" alt="User 2" />
<div class="user-info">
<h3>Jane Smith</h3>
<p class="status">Away</p>
<button class="message-btn">Send Message</button>
</div>
</div>
<!-- ...copied 20 more times across different files -->
Component Approach:
// UserCard.jsx
function UserCard({ user, onMessageClick }) {
return (
<div className="user-card">
<img src={user.avatar} alt={user.name} />
<div className="user-info">
<h3>{user.name}</h3>
<p className={`status ${user.status.toLowerCase()}`}>
{user.status}
</p>
<button
className="message-btn"
onClick={() => onMessageClick(user.id)}
>
Send Message
</button>
</div>
</div>
);
}
// Usage anywhere
function ContactsList({ contacts }) {
return (
<div className="contacts">
{contacts.map(user => (
<UserCard
key={user.id}
user={user}
onMessageClick={handleMessageClick}
/>
))}
</div>
);
}
With this approach:
Need to add a "call" button? One change in UserCard.jsx
Want to show different status colors? Update the CSS class logic once
Need to track analytics on message button clicks? Add it to the onClick handler once
The Ripple Effect
Component thinking didn't just improve my React code - it changed how I approach all frontend development.
Even when I work with other frameworks or vanilla JavaScript, I think in terms of:
Reusable modules
Single responsibility
Clear interfaces
Composition over duplication
This mindset has made me a better developer across all technologies, not just React.
Types of Components: Understanding Your Building Blocks
Not all components are created equal. Over time, I've learned to categorize components based on their purpose and behavior. This helps with organization, naming, and knowing where to put your logic.
UI Components (The Visual Layer)
These are your pure visual elements - they receive props and render UI without any complex logic.
// Button.jsx
function Button({ variant = 'primary', size = 'md', children, onClick }) {
return (
<button
className={`btn btn-${variant} btn-${size}`}
onClick={onClick}
>
{children}
</button>
);
}
// Input.jsx
function Input({ label, type = 'text', placeholder, value, onChange }) {
return (
<div className="input-group">
<label>{label}</label>
<input
type={type}
placeholder={placeholder}
value={value}
onChange={onChange}
/>
</div>
);
}
When to use: For basic visual elements that don't manage their own state.
Layout Components (The Structure)
These components handle positioning, spacing, and overall page structure.
// Container.jsx
function Container({ children, maxWidth = '1200px' }) {
return (
<div className="container" style={{ maxWidth }}>
{children}
</div>
);
}
// Grid.jsx
function Grid({ columns = 3, gap = '1rem', children }) {
return (
<div
className="grid"
style={{
display: 'grid',
gridTemplateColumns: `repeat(${columns}, 1fr)`,
gap
}}
>
{children}
</div>
);
}
When to use: For consistent spacing, layouts, and responsive design patterns.
Stateful Components (The Smart Ones)
These components manage their own internal state and handle user interactions.
// SearchBox.jsx
function SearchBox({ onSearch }) {
const [query, setQuery] = useState('');
const [isLoading, setIsLoading] = useState(false);
const handleSubmit = async (e) => {
e.preventDefault();
setIsLoading(true);
await onSearch(query);
setIsLoading(false);
};
return (
<form onSubmit={handleSubmit}>
<Input
value={query}
onChange={(e) => setQuery(e.target.value)}
placeholder="Search..."
/>
<Button type="submit" disabled={isLoading}>
{isLoading ? 'Searching...' : 'Search'}
</Button>
</form>
);
}
When to use: When you need to manage form inputs, toggles, or any interactive behavior.
Widget Components (The Feature Blocks)
These are self-contained features that combine multiple components and handle their own data fetching.
// UserProfileWidget.jsx
function UserProfileWidget({ userId }) {
const [user, setUser] = useState(null);
const [isLoading, setIsLoading] = useState(true);
useEffect(() => {
fetchUser(userId).then(userData => {
setUser(userData);
setIsLoading(false);
});
}, [userId]);
if (isLoading) return <LoadingSpinner />;
if (!user) return <ErrorMessage message="User not found" />;
return (
<Card>
<Avatar src={user.avatar} alt={user.name} />
<UserInfo name={user.name} email={user.email} />
<UserActions userId={userId} />
</Card>
);
}
When to use: For complete features that can be dropped into any page independently.
Pure Components (The Predictable Ones)
These components always render the same output for the same inputs - no side effects, no state changes.
// ProductCard.jsx (Pure component)
function ProductCard({ product }) {
return (
<div className="product-card">
<img src={product.image} alt={product.name} />
<h3>{product.name}</h3>
<p className="price">${product.price}</p>
<p className="description">{product.description}</p>
</div>
);
}
When to use: For components that are purely presentational and don't need to manage state or side effects.
Code Organization Strategies
How you organize your components can make or break your development experience. Here's the structure I use:
Folder Structure by Type
src/
āāā components/
ā āāā ui/ # Basic UI components
ā ā āāā Button/
ā ā āāā Input/
ā ā āāā Card/
ā āāā layout/ # Layout components
ā ā āāā Container/
ā ā āāā Grid/
ā ā āāā Header/
ā āāā widgets/ # Feature widgets
ā ā āāā UserProfile/
ā ā āāā SearchBox/
ā ā āāā ProductList/
ā āāā pages/ # Page-level components
ā āāā HomePage/
ā āāā ProfilePage/
Individual Component Structure
Each component gets its own folder with everything it needs:
Button/
āāā index.js # Main component file
āāā Button.test.js # Tests
āāā Button.stories.js # Storybook stories (if using)
āāā Button.module.css # Component-specific styles
āāā README.md # Component documentation
The Barrel Export Pattern
Use index.js files to create clean imports:
// components/ui/index.js
export { default as Button } from './Button';
export { default as Input } from './Input';
export { default as Card } from './Card';
// Usage
import { Button, Input, Card } from '@/components/ui';
Naming Strategies
Good naming makes your codebase self-documenting. Here are my rules:
Component Names
PascalCase for component names:
UserProfileCard, notuserProfileCardDescriptive and specific:
SubmitButtoninstead ofButton2Avoid technical jargon:
ProductCardinstead ofProductCardComponent
Props Naming
camelCase for prop names:
onClick,isLoading,maxWidthBoolean props start with
is,has,can, orshould:isVisible,hasError,canEditEvent handlers start with
on:onClick,onSubmit,onUserSelect
File Naming
Component files match component names:
UserProfileCard.jsxUtility files use kebab-case:
api-client.js,date-utils.jsConstants in UPPER_SNAKE_CASE:
API_ENDPOINTS.js
CSS Class Naming
I use BEM (Block Element Modifier) methodology:
/* Block */
.user-card { }
/* Element */
.user-card__avatar { }
.user-card__name { }
.user-card__actions { }
/* Modifier */
.user-card--featured { }
.user-card--compact { }
Practical Naming Examples
// Good component naming
function UserProfileCard({ user, isEditable, onEdit, onDelete }) {
return (
<div className="user-card">
<img
className="user-card__avatar"
src={user.avatar}
alt={user.name}
/>
<div className="user-card__info">
<h3 className="user-card__name">{user.name}</h3>
<p className="user-card__email">{user.email}</p>
</div>
{isEditable && (
<div className="user-card__actions">
<Button onClick={onEdit} variant="secondary">
Edit
</Button>
<Button onClick={onDelete} variant="danger">
Delete
</Button>
</div>
)}
</div>
);
}
// Usage with clear prop names
<UserProfileCard
user={currentUser}
isEditable={userCanEdit}
onEdit={handleUserEdit}
onDelete={handleUserDelete}
/>
The Component Hierarchy
Understanding how components relate to each other helps with both organization and reusability:
Page Component
āāā Layout Components (Header, Sidebar, Footer)
āāā Widget Components (UserProfile, ProductList)
ā āāā UI Components (Card, Button, Avatar)
āāā UI Components (directly used on page)
Flow of data and events:
Props flow down: Parent components pass data to children
Events bubble up: Child components notify parents of interactions
State lives high: Shared state lives in the nearest common parent
This hierarchy helps you decide where to put logic and how to structure your component tree for maximum reusability.
Getting Started with Component Thinking
If you're still in the copy-paste phase, here's how to start thinking in components:
Look for Patterns: Next time you're building a UI, notice when you copy code. Each copy is a potential component.
Start Small: Don't try to componentize everything at once. Start with obvious reusable pieces like buttons, cards, or form inputs.
Practice Decomposition: Take an existing page and try to break it down into components. Draw boxes around reusable pieces.
Think in Composition: Instead of building monolithic pages, practice combining smaller components to create larger ones.
Organize Early: Set up a proper folder structure from the beginning. It's easier to maintain organization than to refactor later.
Common Pitfalls to Avoid
As you start thinking in components, watch out for these mistakes:
Over-componentizing: Not everything needs to be a component. A single <div> with text probably doesn't need its own component file.
Under-componentizing: If you're copying code more than twice, it should probably be a component.
Poor prop design: Avoid passing too many props or unclear prop names. If your component needs 10+ props, consider breaking it down.
Mixing concerns: Don't put data fetching logic in UI components. Keep your concerns separated.
Wrapping Up
Learning component thinking through React was one of those "aha!" moments that fundamentally changed my career. It transformed me from someone who builds websites to someone who builds systems of reusable components.
This mindset shift goes beyond just React - whether you're using Vue, Angular, Svelte, or even vanilla JavaScript, thinking in terms of reusable, well-organized components will make you a better developer.
The key concepts to remember:
Different component types serve different purposes (UI, layout, stateful, widgets, pure)
Proper organization makes your codebase maintainable and scalable
Good naming makes your code self-documenting
Component hierarchy helps you structure data flow and reusability
The next time you're building a UI, ask yourself:
What patterns am I repeating?
How can I break this into smaller, reusable pieces?
What type of component am I building?
How should I organize and name this?
You might feel like you're moving slower at first, setting up proper organization and thinking through component design. But trust me, the long-term benefits in maintainability, consistency, and development speed are absolutely worth it.
A new day, another opportunity to think in components! š


