From Monolith to Modular: Refactoring a React Landing Page Using 'Thinking in React' Principles
A step-by-step case study of transforming a 266-line React component into a maintainable, modular architecture.
The Problem: When Components Grow Too Large
We inherited a React landing page with a single, massive component that violated fundamental React principles. The LandingPage function had ballooned into 266 lines of JSX with inline styles, hardcoded content, and multiple responsibilities in one file.
Before: The Monolith 😱
export default function LandingPage() {
return (
<main style={{ /* 20+ style properties */ }}>
{/* 200+ lines of mixed JSX */}
<div style={{ /* Hero section */ }}>
<h1 style={{ /* Complex gradient styling */ }}>
Chat With Your Files
</h1>
{/* More inline styled elements... */}
</div>
{/* Features, Contact, Footer all mixed together */}
</main>
);
}
This violated several React best practices:
- Single Responsibility Principle: One component handled hero, features, contact, and footer.
- Maintainability: 266 lines made the file hard to navigate.
- Reusability: Inline styles and hardcoded content prevented reuse.
- Testability: Impossible to test sections in isolation.
The Solution: Applying "Thinking in React"
We applied React's Thinking in React methodology to systematically refactor the monolith.
Phase 1: Break the UI into a Component Hierarchy
LandingPage (root container)
├── HeroSection (headline + CTAs)
├── FeaturesSection (feature grid)
├── ContactSection (contact info / lead capture)
└── Footer (branding + credits)
Each component now had a clear responsibility:
- HeroSection – Introduce the product and drive conversions
- FeaturesSection – Highlight key value propositions
- ContactSection – Capture leads and provide contact info
- Footer – Provide branding and credits
Phase 2: Extract Static Components
// HeroSection.tsx
export default function HeroSection() {
return (
<div className={css.heroSection}>
<div className={css.heroContainer}>
<h1 className={css.heroHeadline}>Chat With Your Files</h1>
<p className={css.heroSubtitle}>Turn your document archive into a searchable assistant.</p>
<div className={css.heroButtons}>
<Link href="/demo" className={css.heroPrimaryBtn}>Try Live Demo →</Link>
<Link href="/contact" className={css.heroSecondaryBtn}>Contact Us</Link>
</div>
</div>
</div>
);
}
// LandingPage.tsx
export default function LandingPage() {
return (
<main style={mainStyles}>
<HeroSection />
<FeaturesSection />
<ContactSection />
<Footer />
</main>
);
}
Phase 3: Separate Styling Concerns with CSS Modules
Before (inline styles):
<h1 style={{
fontSize: 56,
fontWeight: 700,
background: "linear-gradient(135deg, #fff 0%, #a0a0a0 100%)",
WebkitBackgroundClip: "text",
WebkitTextFillColor: "transparent"
}}>
After (CSS Modules):
<h1 className={css.heroHeadline}>
/* HeroSection.module.css */
.heroHeadline {
font-size: 56px;
font-weight: 700;
background: linear-gradient(135deg, #fff 0%, #a0a0a0 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
}
The Results: Measurable Improvements
| Metric | Before | After | Improvement | |--------|--------|-------|-------------| | Lines per component | 266 | 20–80 | 70% reduction | | Inline styles | 100% | 0% | Complete elimination | | Reusable components | 0 | 4 | ∞% increase | | CSS modularity | None | Full | Complete separation |
Developer Experience Benefits
Maintainability: Each section is edited independently.
Testability: Components can be tested in isolation.
describe('HeroSection', () => {
it('renders call-to-action buttons', () => {
render(<HeroSection />);
expect(screen.getByText('Try Live Demo')).toBeInTheDocument();
});
});
Collaboration: Multiple devs can work in parallel without conflicts.
Reusability: Components can be dropped into other pages or apps.
Why This Follows React Best Practices
- ✅ Component Hierarchy: Clear parent–child relationships
- ✅ Single Responsibility: One purpose per component
- ✅ Static First: No premature state management
- ✅ Separation of Concerns: CSS, logic, and markup are decoupled
- ✅ Modern Patterns: Functional components, CSS Modules, clean imports
Lessons Learned
When to Apply This Pattern
- Large components (>100 lines)
- Multiple responsibilities in one file
- Inline style sprawl
- Difficult-to-maintain codebases
When Not to Over-Engineer
- Static pages rarely need deep prop plumbing or early abstractions
- Prefer simple, focused components over speculative complexity
Why CSS Modules Help
- Scoped styling (no collisions)
- Solid browser caching performance
- Strong IDE support
- Easier long-term maintenance
Future Improvements
- Add light state where needed (e.g., form validation, mobile menus)
- Make content configurable for A/B tests when appropriate
- Extract a small design system as patterns emerge
- Add TypeScript interfaces/props for type safety as variability grows
Conclusion
By applying Thinking in React, we transformed a 266-line monolithic component into a clean, modular architecture. The refactor improved maintainability, testability, and team productivity—without adding unnecessary complexity.
Key takeaway: Great React architecture starts with identifying the right component boundaries. Sometimes the simplest refactor—breaking things apart thoughtfully—is the most effective.
Tech Stack: React 18, Next.js, TypeScript, CSS Modules Scope: Landing page refactor Effort: ~2 hours Impact: Immediate improvement in maintainability and developer experience